Skip to content

Commit ff0b32e

Browse files
authored
Merge pull request #5212 from s417-lama/legend_itemwidth
Configurable width of the symbol area of legends
2 parents b5caa8f + 1977f12 commit ff0b32e

File tree

8 files changed

+77
-16
lines changed

8 files changed

+77
-16
lines changed

src/components/legend/attributes.js

+8
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,14 @@ module.exports = {
9292
'or remain *constant* independent of the symbol size on the graph.'
9393
].join(' ')
9494
},
95+
itemwidth: {
96+
valType: 'number',
97+
min: 30,
98+
dflt: 30,
99+
role: 'style',
100+
editType: 'legend',
101+
description: 'Sets the width (in px) of the legend item symbols (the part other than the title.text).',
102+
},
95103

96104
itemclick: {
97105
valType: 'enumerated',

src/components/legend/constants.js

-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ module.exports = {
1717

1818
// number of px between legend title and (left) side of legend (always in x direction and from inner border)
1919
titlePad: 2,
20-
// number of px between legend symbol and legend text (always in x direction)
21-
textGap: 40,
2220
// number of px between each legend item (x and/or y direction)
2321
itemGap: 5
2422
};

src/components/legend/defaults.js

+1
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ module.exports = function legendDefaults(layoutIn, layoutOut, fullData) {
113113
if(helpers.isGrouped(layoutOut.legend)) coerce('tracegroupgap');
114114

115115
coerce('itemsizing');
116+
coerce('itemwidth');
116117

117118
coerce('itemclick');
118119
coerce('itemdoubleclick');

src/components/legend/draw.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,8 @@ function drawTexts(g, gd, opts) {
412412
.call(Drawing.font, opts.font)
413413
.text(isEditable ? ensureLength(name, maxNameLength) : name);
414414

415-
svgTextUtils.positionText(textEl, constants.textGap, 0);
415+
var textGap = opts.itemwidth + constants.itemGap * 2;
416+
svgTextUtils.positionText(textEl, textGap, 0);
416417

417418
if(isEditable) {
418419
textEl.call(svgTextUtils.makeEditable, {gd: gd, text: name})
@@ -542,7 +543,8 @@ function computeTextDimensions(g, gd, opts) {
542543
// to avoid getBoundingClientRect
543544
var textY = lineHeight * ((textLines - 1) / 2 - 0.3);
544545
if(legendItem) {
545-
svgTextUtils.positionText(textEl, constants.textGap, -textY);
546+
var textGap = opts.itemwidth + constants.itemGap * 2;
547+
svgTextUtils.positionText(textEl, textGap, -textY);
546548
} else { // case of title
547549
svgTextUtils.positionText(textEl, constants.titlePad + bw, lineHeight + bw);
548550
}
@@ -595,8 +597,8 @@ function computeLegendDimensions(gd, groups, traces, opts) {
595597

596598
var bw = opts.borderwidth;
597599
var bw2 = 2 * bw;
598-
var textGap = constants.textGap;
599600
var itemGap = constants.itemGap;
601+
var textGap = opts.itemwidth + itemGap * 2;
600602
var endPad = 2 * (bw + itemGap);
601603

602604
var yanchor = getYanchor(opts);

src/components/legend/style.js

+16-11
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ var subTypes = require('../../traces/scatter/subtypes');
2020
var stylePie = require('../../traces/pie/style_one');
2121
var pieCastOption = require('../../traces/pie/helpers').castOption;
2222

23+
var constants = require('./constants');
24+
2325
var CST_MARKER_SIZE = 12;
2426
var CST_LINE_WIDTH = 5;
2527
var CST_MARKER_LINE_WIDTH = 2;
@@ -30,6 +32,9 @@ module.exports = function style(s, gd, legend) {
3032
var fullLayout = gd._fullLayout;
3133
if(!legend) legend = fullLayout.legend;
3234
var constantItemSizing = legend.itemsizing === 'constant';
35+
var itemWidth = legend.itemwidth;
36+
var centerPos = (itemWidth + constants.itemGap * 2) / 2;
37+
var centerTransform = 'translate(' + centerPos + ',0)';
3338

3439
var boundLineWidth = function(mlw, cont, max, cst) {
3540
var v;
@@ -161,7 +166,7 @@ module.exports = function style(s, gd, legend) {
161166
.data(showFill || showGradientFill ? [d] : []);
162167
fill.enter().append('path').classed('js-fill', true);
163168
fill.exit().remove();
164-
fill.attr('d', pathStart + 'h30v6h-30z')
169+
fill.attr('d', pathStart + 'h' + itemWidth + 'v6h-' + itemWidth + 'z')
165170
.call(showFill ? Drawing.fillGroupStyle : fillGradient);
166171

167172
if(showLine || showGradientLine) {
@@ -181,7 +186,7 @@ module.exports = function style(s, gd, legend) {
181186
// though there *is* no vertical variation in this case.
182187
// so add an invisibly small angle to the line
183188
// This issue (and workaround) exist across (Mac) Chrome, FF, and Safari
184-
line.attr('d', pathStart + (showGradientLine ? 'l30,0.0001' : 'h30'))
189+
line.attr('d', pathStart + (showGradientLine ? 'l' + itemWidth + ',0.0001' : 'h' + itemWidth))
185190
.call(showLine ? Drawing.lineGroupStyle : lineGradient);
186191
}
187192

@@ -271,7 +276,7 @@ module.exports = function style(s, gd, legend) {
271276
// make sure marker is on the bottom, in case it enters after text
272277
pts.enter().insert('path', ':first-child')
273278
.classed('scatterpts', true)
274-
.attr('transform', 'translate(20,0)');
279+
.attr('transform', centerTransform);
275280
pts.exit().remove();
276281
pts.call(Drawing.pointStyle, tMod, gd);
277282

@@ -283,7 +288,7 @@ module.exports = function style(s, gd, legend) {
283288
.data(showText ? dMod : []);
284289
txt.enter()
285290
.append('g').classed('pointtext', true)
286-
.append('text').attr('transform', 'translate(20,0)');
291+
.append('text').attr('transform', centerTransform);
287292
txt.exit().remove();
288293
txt.selectAll('text').call(Drawing.textPointStyle, tMod, gd);
289294
}
@@ -311,7 +316,7 @@ module.exports = function style(s, gd, legend) {
311316
.selectAll('path.legendwaterfall')
312317
.data(ptsData);
313318
pts.enter().append('path').classed('legendwaterfall', true)
314-
.attr('transform', 'translate(20,0)')
319+
.attr('transform', centerTransform)
315320
.style('stroke-miterlimit', 1);
316321
pts.exit().remove();
317322

@@ -351,7 +356,7 @@ module.exports = function style(s, gd, legend) {
351356
.data(isVisible ? [d] : []);
352357
barpath.enter().append('path').classed('legend' + desiredType, true)
353358
.attr('d', 'M6,6H-6V-6H6Z')
354-
.attr('transform', 'translate(20,0)');
359+
.attr('transform', centerTransform);
355360
barpath.exit().remove();
356361

357362
barpath.each(function(d) {
@@ -375,7 +380,7 @@ module.exports = function style(s, gd, legend) {
375380
pts.enter().append('path').classed('legendbox', true)
376381
// if we want the median bar, prepend M6,0H-6
377382
.attr('d', 'M6,6H-6V-6H6Z')
378-
.attr('transform', 'translate(20,0)');
383+
.attr('transform', centerTransform);
379384
pts.exit().remove();
380385

381386
pts.each(function() {
@@ -415,7 +420,7 @@ module.exports = function style(s, gd, legend) {
415420
if(i) return 'M-15,0H-8M-8,6V-6H8Z'; // increasing
416421
return 'M15,0H8M8,-6V6H-8Z'; // decreasing
417422
})
418-
.attr('transform', 'translate(20,0)')
423+
.attr('transform', centerTransform)
419424
.style('stroke-miterlimit', 1);
420425
pts.exit().remove();
421426

@@ -442,7 +447,7 @@ module.exports = function style(s, gd, legend) {
442447
if(i) return 'M-15,0H0M-8,-6V0'; // increasing
443448
return 'M15,0H0M8,6V0'; // decreasing
444449
})
445-
.attr('transform', 'translate(20,0)')
450+
.attr('transform', centerTransform)
446451
.style('stroke-miterlimit', 1);
447452
pts.exit().remove();
448453

@@ -478,7 +483,7 @@ module.exports = function style(s, gd, legend) {
478483
.data(isVisible ? [d] : []);
479484
pts.enter().append('path').classed('legend' + desiredType, true)
480485
.attr('d', 'M6,6H-6V-6H6Z')
481-
.attr('transform', 'translate(20,0)');
486+
.attr('transform', centerTransform);
482487
pts.exit().remove();
483488

484489
if(pts.size()) {
@@ -576,7 +581,7 @@ module.exports = function style(s, gd, legend) {
576581
.selectAll('path.legend3dandfriends')
577582
.data(ptsData);
578583
pts.enter().append('path').classed('legend3dandfriends', true)
579-
.attr('transform', 'translate(20,0)')
584+
.attr('transform', centerTransform)
580585
.style('stroke-miterlimit', 1);
581586
pts.exit().remove();
582587

Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"data": [
3+
{
4+
"x": [
5+
2,
6+
3,
7+
4,
8+
5
9+
],
10+
"y": [
11+
16,
12+
5,
13+
11,
14+
9
15+
],
16+
"line": {
17+
"dash": "dashdot"
18+
},
19+
"type": "scatter"
20+
},
21+
{
22+
"x": [
23+
1,
24+
2,
25+
3,
26+
4
27+
],
28+
"y": [
29+
12,
30+
9,
31+
15,
32+
12
33+
],
34+
"line": {
35+
"dash": "dash"
36+
},
37+
"type": "scatter"
38+
}
39+
],
40+
"layout": {
41+
"legend": {
42+
"itemwidth": 60
43+
}
44+
}
45+
}

test/jasmine/tests/mock_test.js

+2
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,7 @@ var list = [
669669
'legend_horizontal_one_row',
670670
'legend_horizontal_wrap-alll-lines',
671671
'legend_inside',
672+
'legend_itemwidth_dashline',
672673
'legend_labels',
673674
'legend_large_margin',
674675
'legend_margin-autoexpand-false',
@@ -1733,6 +1734,7 @@ figs['legend_horizontal_groups'] = require('@mocks/legend_horizontal_groups');
17331734
figs['legend_horizontal_one_row'] = require('@mocks/legend_horizontal_one_row');
17341735
figs['legend_horizontal_wrap-alll-lines'] = require('@mocks/legend_horizontal_wrap-alll-lines');
17351736
figs['legend_inside'] = require('@mocks/legend_inside');
1737+
figs['legend_itemwidth_dashline'] = require('@mocks/legend_itemwidth_dashline');
17361738
figs['legend_labels'] = require('@mocks/legend_labels');
17371739
figs['legend_large_margin'] = require('@mocks/legend_large_margin');
17381740
figs['legend_margin-autoexpand-false'] = require('@mocks/legend_margin-autoexpand-false');

0 commit comments

Comments
 (0)