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 00000000000..600c3db1f04 Binary files /dev/null and b/test/fixtures/plugin.legend/borderRadius/legend-border-radius.png differ diff --git a/test/specs/plugin.legend.tests.js b/test/specs/plugin.legend.tests.js index 701b07a82a0..2006dabe0f1 100644 --- a/test/specs/plugin.legend.tests.js +++ b/test/specs/plugin.legend.tests.js @@ -61,6 +61,7 @@ describe('Legend block tests', function() { expect(chart.legend.legendItems).toEqual([{ text: 'dataset1', + borderRadius: 0, fillStyle: '#f31', fontColor: '#666', hidden: false, @@ -76,6 +77,7 @@ describe('Legend block tests', function() { datasetIndex: 0 }, { text: 'dataset2', + borderRadius: 0, fillStyle: 'rgba(0,0,0,0.1)', fontColor: '#666', hidden: true, @@ -91,6 +93,7 @@ describe('Legend block tests', function() { datasetIndex: 1 }, { text: 'dataset3', + borderRadius: 0, fillStyle: 'rgba(0,0,0,0.1)', fontColor: '#666', hidden: false, @@ -137,6 +140,7 @@ describe('Legend block tests', function() { expect(chart.legend.legendItems).toEqual([{ text: 'dataset1', + borderRadius: 0, fillStyle: '#f31', fontColor: '#666', hidden: false, @@ -152,6 +156,7 @@ describe('Legend block tests', function() { datasetIndex: 0 }, { text: 'dataset2', + borderRadius: 0, fillStyle: 'rgba(0,0,0,0.1)', fontColor: '#666', hidden: true, @@ -167,6 +172,7 @@ describe('Legend block tests', function() { datasetIndex: 1 }, { text: 'dataset3', + borderRadius: 0, fillStyle: 'rgba(0,0,0,0.1)', fontColor: '#666', hidden: false, @@ -220,6 +226,7 @@ describe('Legend block tests', function() { expect(chart.legend.legendItems).toEqual([{ text: 'dataset3', + borderRadius: 0, fillStyle: 'rgba(0,0,0,0.1)', fontColor: '#666', hidden: false, @@ -235,6 +242,7 @@ describe('Legend block tests', function() { datasetIndex: 2 }, { text: 'dataset2', + borderRadius: 0, fillStyle: 'rgba(0,0,0,0.1)', fontColor: '#666', hidden: true, @@ -250,6 +258,7 @@ describe('Legend block tests', function() { datasetIndex: 1 }, { text: 'dataset1', + borderRadius: 0, fillStyle: '#f31', fontColor: '#666', hidden: false, @@ -308,6 +317,7 @@ describe('Legend block tests', function() { expect(chart.legend.legendItems).toEqual([{ text: 'dataset1', + borderRadius: 0, fillStyle: '#f31', fontColor: '#666', hidden: false, @@ -323,6 +333,7 @@ describe('Legend block tests', function() { datasetIndex: 0 }, { text: 'dataset3', + borderRadius: 0, fillStyle: 'rgba(0,0,0,0.1)', fontColor: '#666', hidden: false, @@ -380,6 +391,7 @@ describe('Legend block tests', function() { expect(chart.legend.legendItems).toEqual([{ text: 'dataset3', + borderRadius: 0, fillStyle: 'rgba(0,0,0,0.1)', fontColor: '#666', hidden: false, @@ -395,6 +407,7 @@ describe('Legend block tests', function() { datasetIndex: 2 }, { text: 'dataset2', + borderRadius: 0, fillStyle: 'rgba(0,0,0,0.1)', fontColor: '#666', hidden: true, @@ -410,6 +423,7 @@ describe('Legend block tests', function() { datasetIndex: 1 }, { text: 'dataset1', + borderRadius: 0, fillStyle: '#f31', fontColor: '#666', hidden: false, @@ -542,6 +556,7 @@ describe('Legend block tests', function() { expect(chart.legend.legendItems).toEqual([{ text: 'dataset1', + borderRadius: 0, fillStyle: '#f31', fontColor: '#666', hidden: false, @@ -585,6 +600,7 @@ describe('Legend block tests', function() { expect(chart.legend.legendItems).toEqual([{ text: 'dataset1', + borderRadius: 0, fillStyle: 'rgb(50, 0, 0)', fontColor: '#666', hidden: false, @@ -643,6 +659,7 @@ describe('Legend block tests', function() { expect(chart.legend.legendItems).toEqual([{ text: 'dataset1', + borderRadius: 0, fillStyle: 'rgba(0,0,0,0.1)', fontColor: '#666', hidden: false, @@ -658,6 +675,7 @@ describe('Legend block tests', function() { datasetIndex: 0 }, { text: 'dataset2', + borderRadius: 0, fillStyle: '#f31', fontColor: '#666', hidden: false, @@ -717,6 +735,7 @@ describe('Legend block tests', function() { expect(chart.legend.legendItems).toEqual([{ text: 'dataset1', + borderRadius: 0, fillStyle: 'rgba(0,0,0,0.1)', fontColor: '#666', hidden: false, @@ -732,6 +751,7 @@ describe('Legend block tests', function() { datasetIndex: 0 }, { text: 'dataset2', + borderRadius: 0, fillStyle: '#f31', fontColor: '#666', hidden: false, diff --git a/types/index.esm.d.ts b/types/index.esm.d.ts index 71f01be031b..a0535beb3da 100644 --- a/types/index.esm.d.ts +++ b/types/index.esm.d.ts @@ -2009,6 +2009,12 @@ export interface LegendItem { */ text: string; + /** + * Border radius of the legend box + * @since 3.1.0 + */ + borderRadius?: number | BorderRadius; + /** * Index of the associated dataset */