From 7bb017578082cc5e82f71a4329d81ccf11a72990 Mon Sep 17 00:00:00 2001 From: Evert Timberg Date: Wed, 3 Jun 2020 19:55:49 -0400 Subject: [PATCH 1/7] Enable custom legend box heights --- docs/docs/configuration/legend.md | 1 + src/plugins/plugin.legend.js | 38 +++++++++++++++++++++++-------- test/specs/plugin.legend.tests.js | 31 +++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 10 deletions(-) diff --git a/docs/docs/configuration/legend.md b/docs/docs/configuration/legend.md index a0b9523c51f..07c1d23293d 100644 --- a/docs/docs/configuration/legend.md +++ b/docs/docs/configuration/legend.md @@ -49,6 +49,7 @@ The legend label configuration is nested below the legend configuration using th | Name | Type | Default | Description | ---- | ---- | ------- | ----------- | `boxWidth` | `number` | `40` | Width of coloured box. +| `boxHeight` | `number` | fontSize | Height of the coloured box. | `font` | `Font` | `defaults.font` | See [Fonts](fonts.md) | `padding` | `number` | `10` | Padding between rows of colored boxes. | `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. diff --git a/src/plugins/plugin.legend.js b/src/plugins/plugin.legend.js index 0faddddd96b..de6a25ccbbe 100644 --- a/src/plugins/plugin.legend.js +++ b/src/plugins/plugin.legend.js @@ -2,7 +2,7 @@ import defaults from '../core/core.defaults'; import Element from '../core/core.element'; import layouts from '../core/core.layouts'; import {drawPoint} from '../helpers/helpers.canvas'; -import {callback as call, mergeIf, valueOrDefault} from '../helpers/helpers.core'; +import {callback as call, mergeIf, valueOrDefault, isNullOrUndef} from '../helpers/helpers.core'; import {toFont, toPadding} from '../helpers/helpers.options'; import {getRtlAdapter, overrideTextDirection, restoreTextDirection} from '../helpers/helpers.rtl'; @@ -95,6 +95,18 @@ function getBoxWidth(labelOpts, fontSize) { labelOpts.boxWidth; } +/** + * Helper function to get the box height + * @param {object} labelOpts - the label options on the legend + * @param {*} fontSize - the label font size + * @return {number} height of the color box area + */ +function getBoxHeight(labelOpts, fontSize) { + return labelOpts.usePointStyle || isNullOrUndef(labelOpts.boxHeight) ? + fontSize : + labelOpts.boxHeight; +} + export class Legend extends Element { constructor(config) { @@ -272,10 +284,12 @@ export class Legend extends Element { me.legendItems.forEach((legendItem, i) => { const boxWidth = getBoxWidth(labelOpts, fontSize); + const boxHeight = getBoxHeight(labelOpts, fontSize); const width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; + const itemHeight = Math.max(boxHeight, fontSize); if (i === 0 || lineWidths[lineWidths.length - 1] + width + 2 * labelOpts.padding > minSize.width) { - totalHeight += fontSize + labelOpts.padding; + totalHeight += itemHeight + labelOpts.padding; lineWidths[lineWidths.length - (i > 0 ? 0 : 1)] = 0; } @@ -284,7 +298,7 @@ export class Legend extends Element { left: 0, top: 0, width, - height: fontSize + height: itemHeight }; lineWidths[lineWidths.length - 1] += width + labelOpts.padding; @@ -303,6 +317,7 @@ export class Legend extends Element { const heightLimit = minSize.height - titleHeight; me.legendItems.forEach((legendItem, i) => { const boxWidth = getBoxWidth(labelOpts, fontSize); + const boxHeight = getBoxHeight(labelOpts, fontSize); const itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; // If too tall, go to new column @@ -323,7 +338,7 @@ export class Legend extends Element { left: 0, top: 0, width: itemWidth, - height: fontSize + height: Math.max(fontSize, boxHeight) }; }); @@ -377,11 +392,12 @@ export class Legend extends Element { ctx.font = labelFont.string; const boxWidth = getBoxWidth(labelOpts, fontSize); + const boxHeight = getBoxHeight(labelOpts, fontSize); const hitboxes = me.legendHitBoxes; // current position const drawLegendBox = function(x, y, legendItem) { - if (isNaN(boxWidth) || boxWidth <= 0) { + if (isNaN(boxWidth) || boxWidth <= 0 || isNaN(boxHeight) || boxHeight < 0) { return; } @@ -417,9 +433,12 @@ export class Legend extends Element { drawPoint(ctx, drawOptions, centerX, centerY); } else { // Draw box as legend symbol - ctx.fillRect(rtlHelper.leftForLtr(x, boxWidth), y, boxWidth, fontSize); + // Adjust position when boxHeight < fontSize (want it centered) + const yBoxTop = y + Math.max((fontSize - boxHeight) / 2, 0); + + ctx.fillRect(rtlHelper.leftForLtr(x, boxWidth), yBoxTop, boxWidth, boxHeight); if (lineWidth !== 0) { - ctx.strokeRect(rtlHelper.leftForLtr(x, boxWidth), y, boxWidth, fontSize); + ctx.strokeRect(rtlHelper.leftForLtr(x, boxWidth), yBoxTop, boxWidth, boxHeight); } } @@ -429,8 +448,7 @@ export class Legend extends Element { const fillText = function(x, y, legendItem, textWidth) { const halfFontSize = fontSize / 2; const xLeft = rtlHelper.xPlus(x, boxWidth + halfFontSize); - const yMiddle = y + halfFontSize; - + const yMiddle = y + (Math.max(fontSize, boxHeight) / 2); ctx.fillText(legendItem.text, xLeft, yMiddle); if (legendItem.hidden) { @@ -473,7 +491,7 @@ export class Legend extends Element { overrideTextDirection(me.ctx, opts.textDirection); - const itemHeight = fontSize + labelOpts.padding; + const itemHeight = Math.max(fontSize, boxHeight) + labelOpts.padding; me.legendItems.forEach((legendItem, i) => { const textWidth = ctx.measureText(legendItem.text).width; const width = boxWidth + (fontSize / 2) + textWidth; diff --git a/test/specs/plugin.legend.tests.js b/test/specs/plugin.legend.tests.js index 991c81f927e..dcb9b1fef53 100644 --- a/test/specs/plugin.legend.tests.js +++ b/test/specs/plugin.legend.tests.js @@ -373,6 +373,37 @@ describe('Legend block tests', function() { }); }); + it('should draw items with a custom boxHeight', function() { + var chart = window.acquireChart( + { + type: 'line', + data: { + datasets: [{ + label: 'dataset1', + data: [] + }], + labels: [] + }, + options: { + legend: { + position: 'right', + labels: { + boxHeight: 40 + } + } + } + }, + { + canvas: { + width: 512, + height: 105 + } + } + ); + const hitBox = chart.legend.legendHitBoxes[0]; + expect(hitBox.height).toBe(40); + }); + it('should pick up the first item when the property is an array', function() { var chart = window.acquireChart({ type: 'bar', From d7096af7d4f8c08826a97178dc747db98be76652 Mon Sep 17 00:00:00 2001 From: Evert Timberg Date: Thu, 4 Jun 2020 07:44:02 -0400 Subject: [PATCH 2/7] Code review updates --- src/plugins/plugin.legend.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/plugins/plugin.legend.js b/src/plugins/plugin.legend.js index de6a25ccbbe..be3c9462de8 100644 --- a/src/plugins/plugin.legend.js +++ b/src/plugins/plugin.legend.js @@ -102,7 +102,7 @@ function getBoxWidth(labelOpts, fontSize) { * @return {number} height of the color box area */ function getBoxHeight(labelOpts, fontSize) { - return labelOpts.usePointStyle || isNullOrUndef(labelOpts.boxHeight) ? + return labelOpts.usePointStyle && labelOpts.boxHeight ? fontSize : labelOpts.boxHeight; } @@ -393,6 +393,7 @@ export class Legend extends Element { const boxWidth = getBoxWidth(labelOpts, fontSize); const boxHeight = getBoxHeight(labelOpts, fontSize); + const height = Math.max(fontSize, boxHeight); const hitboxes = me.legendHitBoxes; // current position @@ -448,7 +449,7 @@ export class Legend extends Element { const fillText = function(x, y, legendItem, textWidth) { const halfFontSize = fontSize / 2; const xLeft = rtlHelper.xPlus(x, boxWidth + halfFontSize); - const yMiddle = y + (Math.max(fontSize, boxHeight) / 2); + const yMiddle = y + (height / 2); ctx.fillText(legendItem.text, xLeft, yMiddle); if (legendItem.hidden) { @@ -491,7 +492,7 @@ export class Legend extends Element { overrideTextDirection(me.ctx, opts.textDirection); - const itemHeight = Math.max(fontSize, boxHeight) + labelOpts.padding; + const itemHeight = height + labelOpts.padding; me.legendItems.forEach((legendItem, i) => { const textWidth = ctx.measureText(legendItem.text).width; const width = boxWidth + (fontSize / 2) + textWidth; From fee66cb3cec5f81af516417eb776332f971065e2 Mon Sep 17 00:00:00 2001 From: Evert Timberg Date: Thu, 4 Jun 2020 07:54:06 -0400 Subject: [PATCH 3/7] Refactor to reduce copied code --- src/plugins/plugin.legend.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/plugins/plugin.legend.js b/src/plugins/plugin.legend.js index be3c9462de8..8469ac1769e 100644 --- a/src/plugins/plugin.legend.js +++ b/src/plugins/plugin.legend.js @@ -2,7 +2,7 @@ import defaults from '../core/core.defaults'; import Element from '../core/core.element'; import layouts from '../core/core.layouts'; import {drawPoint} from '../helpers/helpers.canvas'; -import {callback as call, mergeIf, valueOrDefault, isNullOrUndef} from '../helpers/helpers.core'; +import {callback as call, mergeIf, valueOrDefault} from '../helpers/helpers.core'; import {toFont, toPadding} from '../helpers/helpers.options'; import {getRtlAdapter, overrideTextDirection, restoreTextDirection} from '../helpers/helpers.rtl'; @@ -251,6 +251,9 @@ export class Legend extends Element { const ctx = me.ctx; const labelFont = toFont(labelOpts.font); const fontSize = labelFont.size; + const boxWidth = getBoxWidth(labelOpts, fontSize); + const boxHeight = getBoxHeight(labelOpts, fontSize); + const itemHeight = Math.max(boxHeight, fontSize); // Reset hit boxes const hitboxes = me.legendHitBoxes = []; @@ -283,10 +286,7 @@ export class Legend extends Element { ctx.textBaseline = 'middle'; me.legendItems.forEach((legendItem, i) => { - const boxWidth = getBoxWidth(labelOpts, fontSize); - const boxHeight = getBoxHeight(labelOpts, fontSize); const width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; - const itemHeight = Math.max(boxHeight, fontSize); if (i === 0 || lineWidths[lineWidths.length - 1] + width + 2 * labelOpts.padding > minSize.width) { totalHeight += itemHeight + labelOpts.padding; @@ -316,8 +316,6 @@ export class Legend extends Element { const heightLimit = minSize.height - titleHeight; me.legendItems.forEach((legendItem, i) => { - const boxWidth = getBoxWidth(labelOpts, fontSize); - const boxHeight = getBoxHeight(labelOpts, fontSize); const itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; // If too tall, go to new column @@ -338,7 +336,7 @@ export class Legend extends Element { left: 0, top: 0, width: itemWidth, - height: Math.max(fontSize, boxHeight) + height: itemHeight, }; }); From 4b80fa3478c4061247f371690cf2394953c67e16 Mon Sep 17 00:00:00 2001 From: Evert Timberg Date: Thu, 4 Jun 2020 08:02:26 -0400 Subject: [PATCH 4/7] Update getBoxHeight --- src/plugins/plugin.legend.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/plugin.legend.js b/src/plugins/plugin.legend.js index 8469ac1769e..6d0bde07fae 100644 --- a/src/plugins/plugin.legend.js +++ b/src/plugins/plugin.legend.js @@ -102,7 +102,7 @@ function getBoxWidth(labelOpts, fontSize) { * @return {number} height of the color box area */ function getBoxHeight(labelOpts, fontSize) { - return labelOpts.usePointStyle && labelOpts.boxHeight ? + return labelOpts.usePointStyle && labelOpts.boxHeight > fontSize ? fontSize : labelOpts.boxHeight; } From 0b603a043cbef651c78121c79d0fc6be8ca8cb4e Mon Sep 17 00:00:00 2001 From: Evert Timberg Date: Thu, 4 Jun 2020 08:14:42 -0400 Subject: [PATCH 5/7] Handle unset boxHeight --- src/plugins/plugin.legend.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/plugin.legend.js b/src/plugins/plugin.legend.js index 6d0bde07fae..7a9361c871b 100644 --- a/src/plugins/plugin.legend.js +++ b/src/plugins/plugin.legend.js @@ -2,7 +2,7 @@ import defaults from '../core/core.defaults'; import Element from '../core/core.element'; import layouts from '../core/core.layouts'; import {drawPoint} from '../helpers/helpers.canvas'; -import {callback as call, mergeIf, valueOrDefault} from '../helpers/helpers.core'; +import {callback as call, mergeIf, valueOrDefault, isNullOrUndef} from '../helpers/helpers.core'; import {toFont, toPadding} from '../helpers/helpers.options'; import {getRtlAdapter, overrideTextDirection, restoreTextDirection} from '../helpers/helpers.rtl'; @@ -102,7 +102,8 @@ function getBoxWidth(labelOpts, fontSize) { * @return {number} height of the color box area */ function getBoxHeight(labelOpts, fontSize) { - return labelOpts.usePointStyle && labelOpts.boxHeight > fontSize ? + const {boxHeight} = labelOpts; + return (labelOpts.usePointStyle && boxHeight) || isNullOrUndef(boxHeight) > fontSize ? fontSize : labelOpts.boxHeight; } From eec7e6c1183fb6c803282f9ef3c51aed1210459d Mon Sep 17 00:00:00 2001 From: Evert Timberg Date: Thu, 4 Jun 2020 08:23:40 -0400 Subject: [PATCH 6/7] Box Height correction --- src/plugins/plugin.legend.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/plugin.legend.js b/src/plugins/plugin.legend.js index 7a9361c871b..909bd725043 100644 --- a/src/plugins/plugin.legend.js +++ b/src/plugins/plugin.legend.js @@ -103,7 +103,7 @@ function getBoxWidth(labelOpts, fontSize) { */ function getBoxHeight(labelOpts, fontSize) { const {boxHeight} = labelOpts; - return (labelOpts.usePointStyle && boxHeight) || isNullOrUndef(boxHeight) > fontSize ? + return (labelOpts.usePointStyle && boxHeight > fontSize) || isNullOrUndef(boxHeight) ? fontSize : labelOpts.boxHeight; } From bd551f7c798a406ceb366f2e46c38565d46e61d0 Mon Sep 17 00:00:00 2001 From: Evert Timberg Date: Thu, 4 Jun 2020 17:00:08 -0400 Subject: [PATCH 7/7] Code review updates --- src/plugins/plugin.legend.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/plugins/plugin.legend.js b/src/plugins/plugin.legend.js index 909bd725043..9a6b93ffb9f 100644 --- a/src/plugins/plugin.legend.js +++ b/src/plugins/plugin.legend.js @@ -90,9 +90,10 @@ defaults.set('legend', { * @return {number} width of the color box area */ function getBoxWidth(labelOpts, fontSize) { - return labelOpts.usePointStyle && labelOpts.boxWidth > fontSize ? + const {boxWidth} = labelOpts; + return (labelOpts.usePointStyle && boxWidth > fontSize) || isNullOrUndef(boxWidth) ? fontSize : - labelOpts.boxWidth; + boxWidth; } /** @@ -105,7 +106,7 @@ function getBoxHeight(labelOpts, fontSize) { const {boxHeight} = labelOpts; return (labelOpts.usePointStyle && boxHeight > fontSize) || isNullOrUndef(boxHeight) ? fontSize : - labelOpts.boxHeight; + boxHeight; } export class Legend extends Element {