From 4cae5e64a6bb06727800949a776aef53b575b053 Mon Sep 17 00:00:00 2001 From: Evert Timberg Date: Sat, 10 Apr 2021 14:05:22 -0400 Subject: [PATCH] Legend boxes support borderRadius --- docs/configuration/legend.md | 4 ++ src/plugins/plugin.legend.js | 24 +++++++- .../borderRadius/legend-border-radius.js | 55 ++++++++++++++++++ .../borderRadius/legend-border-radius.png | Bin 0 -> 15307 bytes test/specs/plugin.legend.tests.js | 20 +++++++ types/index.esm.d.ts | 6 ++ 6 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 test/fixtures/plugin.legend/borderRadius/legend-border-radius.js create mode 100644 test/fixtures/plugin.legend/borderRadius/legend-border-radius.png diff --git a/docs/configuration/legend.md b/docs/configuration/legend.md index e1ff2cbf869..0be48c975b5 100644 --- a/docs/configuration/legend.md +++ b/docs/configuration/legend.md @@ -84,6 +84,10 @@ Items passed to the legend `onClick` function are the ones returned from `labels // Label that will be displayed text: string, + // Border radius of the legend item. + // Introduced in 3.1.0 + borderRadius?: number | BorderRadius, + // Index of the associated dataset datasetIndex: number, diff --git a/src/plugins/plugin.legend.js b/src/plugins/plugin.legend.js index e1d3c65c1d6..b30ba41d301 100644 --- a/src/plugins/plugin.legend.js +++ b/src/plugins/plugin.legend.js @@ -1,13 +1,14 @@ import defaults from '../core/core.defaults'; import Element from '../core/core.element'; import layouts from '../core/core.layouts'; -import {drawPoint, renderText} from '../helpers/helpers.canvas'; +import {addRoundedRectPath, drawPoint, renderText} from '../helpers/helpers.canvas'; import { callback as call, valueOrDefault, toFont, toPadding, getRtlAdapter, overrideTextDirection, restoreTextDirection, clipArea, unclipArea } from '../helpers/index'; import {_toLeftRightCenter, _alignStartEnd, _textX} from '../helpers/helpers.extras'; +import {toTRBLCorners} from '../helpers/helpers.options'; /** * @typedef { import("../platform/platform.base").ChartEvent } ChartEvent */ @@ -341,10 +342,26 @@ export class Legend extends Element { // Draw box as legend symbol // Adjust position when boxHeight < fontSize (want it centered) const yBoxTop = y + Math.max((fontSize - boxHeight) / 2, 0); + const xBoxLeft = rtlHelper.leftForLtr(x, boxWidth); + const borderRadius = toTRBLCorners(legendItem.borderRadius); + + ctx.beginPath(); + + if (Object.values(borderRadius).some(v => v !== 0)) { + addRoundedRectPath(ctx, { + x: xBoxLeft, + y: yBoxTop, + w: boxWidth, + h: boxHeight, + radius: borderRadius, + }); + } else { + ctx.rect(xBoxLeft, yBoxTop, boxWidth, boxHeight); + } - ctx.fillRect(rtlHelper.leftForLtr(x, boxWidth), yBoxTop, boxWidth, boxHeight); + ctx.fill(); if (lineWidth !== 0) { - ctx.strokeRect(rtlHelper.leftForLtr(x, boxWidth), yBoxTop, boxWidth, boxHeight); + ctx.stroke(); } } @@ -653,6 +670,7 @@ export default { pointStyle: pointStyle || style.pointStyle, rotation: style.rotation, textAlign: textAlign || style.textAlign, + borderRadius: 0, // TODO: v4, default to style.borderRadius // Below is extra data used for toggling the datasets datasetIndex: meta.index diff --git a/test/fixtures/plugin.legend/borderRadius/legend-border-radius.js b/test/fixtures/plugin.legend/borderRadius/legend-border-radius.js new file mode 100644 index 00000000000..d765134ba30 --- /dev/null +++ b/test/fixtures/plugin.legend/borderRadius/legend-border-radius.js @@ -0,0 +1,55 @@ +module.exports = { + config: { + type: 'line', + data: { + labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'], + datasets: [ + { + label: '# of Votes', + data: [12, 19, 3, 5, 2, 3], + borderWidth: 1, + borderColor: '#FF0000', + backgroundColor: '#00FF00', + }, + { + label: '# of Points', + data: [7, 11, 5, 8, 3, 7], + borderWidth: 2, + borderColor: '#FF00FF', + backgroundColor: '#0000FF', + } + ] + }, + options: { + scales: { + x: {display: false}, + y: {display: false} + }, + plugins: { + title: false, + tooltip: false, + filler: false, + legend: { + labels: { + generateLabels: (chart) => { + const items = Chart.defaults.plugins.legend.labels.generateLabels(chart); + + for (const item of items) { + item.borderRadius = 5; + } + + return items; + } + } + } + } + } + }, + options: { + spriteText: true, + canvas: { + width: 512, + height: 256 + } + } +}; diff --git a/test/fixtures/plugin.legend/borderRadius/legend-border-radius.png b/test/fixtures/plugin.legend/borderRadius/legend-border-radius.png new file mode 100644 index 0000000000000000000000000000000000000000..600c3db1f049d8d7c5f79b04115cc16b84da95aa GIT binary patch literal 15307 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