Skip to content

Commit 0703d78

Browse files
authored
Enable custom legend box heights (chartjs#7459)
Enable custom legend box heights
1 parent 690d07b commit 0703d78

File tree

3 files changed

+65
-14
lines changed

3 files changed

+65
-14
lines changed

docs/docs/configuration/legend.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ The legend label configuration is nested below the legend configuration using th
4949
| Name | Type | Default | Description
5050
| ---- | ---- | ------- | -----------
5151
| `boxWidth` | `number` | `40` | Width of coloured box.
52+
| `boxHeight` | `number` | fontSize | Height of the coloured box.
5253
| `font` | `Font` | `defaults.font` | See [Fonts](fonts.md)
5354
| `padding` | `number` | `10` | Padding between rows of colored boxes.
5455
| `generateLabels` | `function` | | Generates legend items for each thing in the legend. Default implementation returns the text + styling for the color box. See [Legend Item](#legend-item-interface) for details.

src/plugins/plugin.legend.js

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import defaults from '../core/core.defaults';
22
import Element from '../core/core.element';
33
import layouts from '../core/core.layouts';
44
import {drawPoint} from '../helpers/helpers.canvas';
5-
import {callback as call, mergeIf, valueOrDefault} from '../helpers/helpers.core';
5+
import {callback as call, mergeIf, valueOrDefault, isNullOrUndef} from '../helpers/helpers.core';
66
import {toFont, toPadding} from '../helpers/helpers.options';
77
import {getRtlAdapter, overrideTextDirection, restoreTextDirection} from '../helpers/helpers.rtl';
88

@@ -90,9 +90,23 @@ defaults.set('legend', {
9090
* @return {number} width of the color box area
9191
*/
9292
function getBoxWidth(labelOpts, fontSize) {
93-
return labelOpts.usePointStyle && labelOpts.boxWidth > fontSize ?
93+
const {boxWidth} = labelOpts;
94+
return (labelOpts.usePointStyle && boxWidth > fontSize) || isNullOrUndef(boxWidth) ?
9495
fontSize :
95-
labelOpts.boxWidth;
96+
boxWidth;
97+
}
98+
99+
/**
100+
* Helper function to get the box height
101+
* @param {object} labelOpts - the label options on the legend
102+
* @param {*} fontSize - the label font size
103+
* @return {number} height of the color box area
104+
*/
105+
function getBoxHeight(labelOpts, fontSize) {
106+
const {boxHeight} = labelOpts;
107+
return (labelOpts.usePointStyle && boxHeight > fontSize) || isNullOrUndef(boxHeight) ?
108+
fontSize :
109+
boxHeight;
96110
}
97111

98112
export class Legend extends Element {
@@ -239,6 +253,9 @@ export class Legend extends Element {
239253
const ctx = me.ctx;
240254
const labelFont = toFont(labelOpts.font);
241255
const fontSize = labelFont.size;
256+
const boxWidth = getBoxWidth(labelOpts, fontSize);
257+
const boxHeight = getBoxHeight(labelOpts, fontSize);
258+
const itemHeight = Math.max(boxHeight, fontSize);
242259

243260
// Reset hit boxes
244261
const hitboxes = me.legendHitBoxes = [];
@@ -271,11 +288,10 @@ export class Legend extends Element {
271288
ctx.textBaseline = 'middle';
272289

273290
me.legendItems.forEach((legendItem, i) => {
274-
const boxWidth = getBoxWidth(labelOpts, fontSize);
275291
const width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
276292

277293
if (i === 0 || lineWidths[lineWidths.length - 1] + width + 2 * labelOpts.padding > minSize.width) {
278-
totalHeight += fontSize + labelOpts.padding;
294+
totalHeight += itemHeight + labelOpts.padding;
279295
lineWidths[lineWidths.length - (i > 0 ? 0 : 1)] = 0;
280296
}
281297

@@ -284,7 +300,7 @@ export class Legend extends Element {
284300
left: 0,
285301
top: 0,
286302
width,
287-
height: fontSize
303+
height: itemHeight
288304
};
289305

290306
lineWidths[lineWidths.length - 1] += width + labelOpts.padding;
@@ -302,7 +318,6 @@ export class Legend extends Element {
302318

303319
const heightLimit = minSize.height - titleHeight;
304320
me.legendItems.forEach((legendItem, i) => {
305-
const boxWidth = getBoxWidth(labelOpts, fontSize);
306321
const itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
307322

308323
// If too tall, go to new column
@@ -323,7 +338,7 @@ export class Legend extends Element {
323338
left: 0,
324339
top: 0,
325340
width: itemWidth,
326-
height: fontSize
341+
height: itemHeight,
327342
};
328343
});
329344

@@ -377,11 +392,13 @@ export class Legend extends Element {
377392
ctx.font = labelFont.string;
378393

379394
const boxWidth = getBoxWidth(labelOpts, fontSize);
395+
const boxHeight = getBoxHeight(labelOpts, fontSize);
396+
const height = Math.max(fontSize, boxHeight);
380397
const hitboxes = me.legendHitBoxes;
381398

382399
// current position
383400
const drawLegendBox = function(x, y, legendItem) {
384-
if (isNaN(boxWidth) || boxWidth <= 0) {
401+
if (isNaN(boxWidth) || boxWidth <= 0 || isNaN(boxHeight) || boxHeight < 0) {
385402
return;
386403
}
387404

@@ -417,9 +434,12 @@ export class Legend extends Element {
417434
drawPoint(ctx, drawOptions, centerX, centerY);
418435
} else {
419436
// Draw box as legend symbol
420-
ctx.fillRect(rtlHelper.leftForLtr(x, boxWidth), y, boxWidth, fontSize);
437+
// Adjust position when boxHeight < fontSize (want it centered)
438+
const yBoxTop = y + Math.max((fontSize - boxHeight) / 2, 0);
439+
440+
ctx.fillRect(rtlHelper.leftForLtr(x, boxWidth), yBoxTop, boxWidth, boxHeight);
421441
if (lineWidth !== 0) {
422-
ctx.strokeRect(rtlHelper.leftForLtr(x, boxWidth), y, boxWidth, fontSize);
442+
ctx.strokeRect(rtlHelper.leftForLtr(x, boxWidth), yBoxTop, boxWidth, boxHeight);
423443
}
424444
}
425445

@@ -429,8 +449,7 @@ export class Legend extends Element {
429449
const fillText = function(x, y, legendItem, textWidth) {
430450
const halfFontSize = fontSize / 2;
431451
const xLeft = rtlHelper.xPlus(x, boxWidth + halfFontSize);
432-
const yMiddle = y + halfFontSize;
433-
452+
const yMiddle = y + (height / 2);
434453
ctx.fillText(legendItem.text, xLeft, yMiddle);
435454

436455
if (legendItem.hidden) {
@@ -473,7 +492,7 @@ export class Legend extends Element {
473492

474493
overrideTextDirection(me.ctx, opts.textDirection);
475494

476-
const itemHeight = fontSize + labelOpts.padding;
495+
const itemHeight = height + labelOpts.padding;
477496
me.legendItems.forEach((legendItem, i) => {
478497
const textWidth = ctx.measureText(legendItem.text).width;
479498
const width = boxWidth + (fontSize / 2) + textWidth;

test/specs/plugin.legend.tests.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,37 @@ describe('Legend block tests', function() {
373373
});
374374
});
375375

376+
it('should draw items with a custom boxHeight', function() {
377+
var chart = window.acquireChart(
378+
{
379+
type: 'line',
380+
data: {
381+
datasets: [{
382+
label: 'dataset1',
383+
data: []
384+
}],
385+
labels: []
386+
},
387+
options: {
388+
legend: {
389+
position: 'right',
390+
labels: {
391+
boxHeight: 40
392+
}
393+
}
394+
}
395+
},
396+
{
397+
canvas: {
398+
width: 512,
399+
height: 105
400+
}
401+
}
402+
);
403+
const hitBox = chart.legend.legendHitBoxes[0];
404+
expect(hitBox.height).toBe(40);
405+
});
406+
376407
it('should pick up the first item when the property is an array', function() {
377408
var chart = window.acquireChart({
378409
type: 'bar',

0 commit comments

Comments
 (0)