]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Enable custom legend box heights (#7459)
authorEvert Timberg <evert.timberg+github@gmail.com>
Thu, 4 Jun 2020 23:53:40 +0000 (19:53 -0400)
committerGitHub <noreply@github.com>
Thu, 4 Jun 2020 23:53:40 +0000 (19:53 -0400)
Enable custom legend box heights

docs/docs/configuration/legend.md
src/plugins/plugin.legend.js
test/specs/plugin.legend.tests.js

index a0b9523c51fdcf447f63ab7f02cf67a8266594b4..07c1d23293db03de80fe3a86243e03010c940d9a 100644 (file)
@@ -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.
index 0faddddd96b7d4dcb1e5bb0b379314355608ff87..9a6b93ffb9fc4933257098c89e5e41110406d1a0 100644 (file)
@@ -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';
 
@@ -90,9 +90,23 @@ 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;
+}
+
+/**
+ * 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) {
+       const {boxHeight} = labelOpts;
+       return (labelOpts.usePointStyle && boxHeight > fontSize) || isNullOrUndef(boxHeight) ?
+               fontSize :
+               boxHeight;
 }
 
 export class Legend extends Element {
@@ -239,6 +253,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 = [];
@@ -271,11 +288,10 @@ export class Legend extends Element {
                        ctx.textBaseline = 'middle';
 
                        me.legendItems.forEach((legendItem, i) => {
-                               const boxWidth = getBoxWidth(labelOpts, fontSize);
                                const width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
 
                                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 +300,7 @@ export class Legend extends Element {
                                        left: 0,
                                        top: 0,
                                        width,
-                                       height: fontSize
+                                       height: itemHeight
                                };
 
                                lineWidths[lineWidths.length - 1] += width + labelOpts.padding;
@@ -302,7 +318,6 @@ export class Legend extends Element {
 
                        const heightLimit = minSize.height - titleHeight;
                        me.legendItems.forEach((legendItem, i) => {
-                               const boxWidth = getBoxWidth(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: itemHeight,
                                };
                        });
 
@@ -377,11 +392,13 @@ export class Legend extends Element {
                ctx.font = labelFont.string;
 
                const boxWidth = getBoxWidth(labelOpts, fontSize);
+               const boxHeight = getBoxHeight(labelOpts, fontSize);
+               const height = Math.max(fontSize, boxHeight);
                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 +434,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 +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 + halfFontSize;
-
+                       const yMiddle = y + (height / 2);
                        ctx.fillText(legendItem.text, xLeft, yMiddle);
 
                        if (legendItem.hidden) {
@@ -473,7 +492,7 @@ export class Legend extends Element {
 
                overrideTextDirection(me.ctx, opts.textDirection);
 
-               const itemHeight = fontSize + 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;
index 991c81f927e13b5042da61dc9e35d8cc7dcaeb99..dcb9b1fef539b60fe2df17e6c18092e8924fbdf8 100644 (file)
@@ -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',