From 0d3a907f6e68ac7860431fea70ac2df1e005b6d3 Mon Sep 17 00:00:00 2001 From: Akihiko Kusanagi Date: Tue, 12 Feb 2019 23:50:13 +0800 Subject: [PATCH] Add support for scriptable options --- docs/configuration/legend.md | 5 +- src/controllers/controller.doughnut.js | 16 +- src/controllers/controller.polarArea.js | 16 +- src/plugins/plugin.legend.js | 52 +++---- test/specs/plugin.legend.tests.js | 193 +++++++++++++++--------- 5 files changed, 163 insertions(+), 119 deletions(-) diff --git a/docs/configuration/legend.md b/docs/configuration/legend.md index 30e3283b40d..f0ea0100223 100644 --- a/docs/configuration/legend.md +++ b/docs/configuration/legend.md @@ -72,7 +72,10 @@ Items passed to the legend `onClick` function are the ones returned from `labels strokeStyle: Color, // Point style of the legend box (only used if usePointStyle is true) - pointStyle: string + pointStyle: string | Image, + + // Rotation of the point in degrees (only used if usePointStyle is true) + rotation: number } ``` diff --git a/src/controllers/controller.doughnut.js b/src/controllers/controller.doughnut.js index 21e23ebcae8..0acff555e24 100644 --- a/src/controllers/controller.doughnut.js +++ b/src/controllers/controller.doughnut.js @@ -45,20 +45,14 @@ defaults._set('doughnut', { if (data.labels.length && data.datasets.length) { return data.labels.map(function(label, i) { var meta = chart.getDatasetMeta(0); - var ds = data.datasets[0]; - var arc = meta.data[i]; - var custom = arc && arc.custom || {}; - var arcOpts = chart.options.elements.arc; - var fill = resolve([custom.backgroundColor, ds.backgroundColor, arcOpts.backgroundColor], undefined, i); - var stroke = resolve([custom.borderColor, ds.borderColor, arcOpts.borderColor], undefined, i); - var bw = resolve([custom.borderWidth, ds.borderWidth, arcOpts.borderWidth], undefined, i); + var model = meta.controller._resolveElementOptions(meta.data[i] || {}, i); return { text: label, - fillStyle: fill, - strokeStyle: stroke, - lineWidth: bw, - hidden: isNaN(ds.data[i]) || meta.data[i].hidden, + fillStyle: model.backgroundColor, + strokeStyle: model.borderColor, + lineWidth: model.borderWidth, + hidden: isNaN(data.datasets[0].data[i]) || meta.data[i].hidden, // Extra data used for toggling the correct item index: i diff --git a/src/controllers/controller.polarArea.js b/src/controllers/controller.polarArea.js index 19ceefddab7..688367e0a9c 100644 --- a/src/controllers/controller.polarArea.js +++ b/src/controllers/controller.polarArea.js @@ -59,20 +59,14 @@ defaults._set('polarArea', { if (data.labels.length && data.datasets.length) { return data.labels.map(function(label, i) { var meta = chart.getDatasetMeta(0); - var ds = data.datasets[0]; - var arc = meta.data[i]; - var custom = arc.custom || {}; - var arcOpts = chart.options.elements.arc; - var fill = resolve([custom.backgroundColor, ds.backgroundColor, arcOpts.backgroundColor], undefined, i); - var stroke = resolve([custom.borderColor, ds.borderColor, arcOpts.borderColor], undefined, i); - var bw = resolve([custom.borderWidth, ds.borderWidth, arcOpts.borderWidth], undefined, i); + var model = meta.controller._resolveElementOptions(meta.data[i] || {}, i); return { text: label, - fillStyle: fill, - strokeStyle: stroke, - lineWidth: bw, - hidden: isNaN(ds.data[i]) || meta.data[i].hidden, + fillStyle: model.backgroundColor, + strokeStyle: model.borderColor, + lineWidth: model.borderWidth, + hidden: isNaN(data.datasets[0].data[i]) || meta.data[i].hidden, // Extra data used for toggling the correct item index: i diff --git a/src/plugins/plugin.legend.js b/src/plugins/plugin.legend.js index 5fb3658fc5c..73ad47f5a1a 100644 --- a/src/plugins/plugin.legend.js +++ b/src/plugins/plugin.legend.js @@ -7,7 +7,6 @@ var layouts = require('../core/core.layouts'); var noop = helpers.noop; var valueOrDefault = helpers.valueOrDefault; -var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; defaults._set('global', { legend: { @@ -49,34 +48,35 @@ defaults._set('global', { generateLabels: function(chart) { var data = chart.data; return helpers.isArray(data.datasets) ? data.datasets.map(function(dataset, i) { - var type = chart.getDatasetMeta(i).type; - var options = chart.options; - var legendOpts = options.legend; - + var meta = chart.getDatasetMeta(i); + var controller = meta.controller; + var legendOpts = chart.options.legend; var usePointStyle = legendOpts && legendOpts.labels && legendOpts.labels.usePointStyle; - var backgroundColor = usePointStyle ? valueAtIndexOrDefault(dataset.pointBackgroundColor, 0, dataset.backgroundColor) : dataset.backgroundColor; - var borderWidth = usePointStyle ? valueAtIndexOrDefault(dataset.pointBorderWidth, 0, dataset.borderWidth) : dataset.borderWidth; - var borderColor = usePointStyle ? valueAtIndexOrDefault(dataset.pointBorderColor, 0, dataset.borderColor) : dataset.borderColor; - - var useLineStyles = (type === 'line' || type === 'radar') && !usePointStyle; - var borderCapStyle = useLineStyles ? dataset.borderCapStyle : 'butt'; - var borderDash = useLineStyles ? dataset.borderDash : []; - var borderDashOffset = useLineStyles ? dataset.borderDashOffset : 0; - var borderJoinStyle = useLineStyles ? dataset.borderJoinStyle : 'miter'; - - var elementOpts = options.elements[useLineStyles ? 'line' : type === 'bar' ? 'rectangle' : 'point']; + var model; + + if (controller._resolveLineOptions && !usePointStyle) { + model = controller._resolveLineOptions(meta.dataset || {}); + if (model.fill === false || model.fill === null) { + model.backgroundColor = 'rgba(0,0,0,0)'; + } + } else if (controller._resolvePointOptions) { + model = controller._resolvePointOptions(meta.data[0] || {}, 0); + } else { + model = controller._resolveElementOptions(meta.data[0] || {}, 0); + } return { text: dataset.label, - fillStyle: valueAtIndexOrDefault(backgroundColor, 0, elementOpts.backgroundColor), + fillStyle: model.backgroundColor, hidden: !chart.isDatasetVisible(i), - lineCap: valueOrDefault(borderCapStyle, elementOpts.borderCapStyle), - lineDash: valueOrDefault(borderDash, elementOpts.borderDash), - lineDashOffset: valueOrDefault(borderDashOffset, elementOpts.borderDashOffset), - lineJoin: valueOrDefault(borderJoinStyle, elementOpts.borderJoinStyle), - lineWidth: valueAtIndexOrDefault(borderWidth, 0, elementOpts.borderWidth), - strokeStyle: valueAtIndexOrDefault(borderColor, 0, elementOpts.borderColor), - pointStyle: valueAtIndexOrDefault(dataset.pointStyle, 0, elementOpts.pointStyle), + lineCap: model.borderCapStyle, + lineDash: model.borderDash, + lineDashOffset: model.borderDashOffset, + lineJoin: model.borderJoinStyle, + lineWidth: model.borderWidth, + strokeStyle: model.borderColor, + pointStyle: model.pointStyle, + rotation: model.rotation, // Below is extra data used for toggling the datasets datasetIndex: i @@ -384,7 +384,7 @@ var Legend = Element.extend({ ctx.setLineDash(valueOrDefault(legendItem.lineDash, lineDefault.borderDash)); } - if (opts.labels && opts.labels.usePointStyle) { + if (labelOpts && labelOpts.usePointStyle) { // Recalculate x and y for drawPoint() because its expecting // x and y to be center of figure (instead of top left) var radius = boxWidth * Math.SQRT2 / 2; @@ -392,7 +392,7 @@ var Legend = Element.extend({ var centerY = y + fontSize / 2; // Draw pointStyle as legend symbol - helpers.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY); + helpers.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY, legendItem.rotation); } else { // Draw box as legend symbol ctx.fillRect(x, y, boxWidth, fontSize); diff --git a/test/specs/plugin.legend.tests.js b/test/specs/plugin.legend.tests.js index c2b960e9b6b..3967c8f5ee5 100644 --- a/test/specs/plugin.legend.tests.js +++ b/test/specs/plugin.legend.tests.js @@ -27,14 +27,14 @@ describe('Legend block tests', function() { datasets: [{ label: 'dataset1', backgroundColor: '#f31', - borderCapStyle: 'round', + borderCapStyle: 'butt', borderDash: [2, 2], borderDashOffset: 5.5, data: [] }, { label: 'dataset2', hidden: true, - borderJoinStyle: 'round', + borderJoinStyle: 'miter', data: [] }, { label: 'dataset3', @@ -51,37 +51,40 @@ describe('Legend block tests', function() { text: 'dataset1', fillStyle: '#f31', hidden: false, - lineCap: 'butt', - lineDash: [], - lineDashOffset: 0, - lineJoin: 'miter', + lineCap: undefined, + lineDash: undefined, + lineDashOffset: undefined, + lineJoin: undefined, lineWidth: 0, strokeStyle: 'rgba(0,0,0,0.1)', pointStyle: undefined, + rotation: undefined, datasetIndex: 0 }, { text: 'dataset2', fillStyle: 'rgba(0,0,0,0.1)', hidden: true, - lineCap: 'butt', - lineDash: [], - lineDashOffset: 0, - lineJoin: 'miter', + lineCap: undefined, + lineDash: undefined, + lineDashOffset: undefined, + lineJoin: undefined, lineWidth: 0, strokeStyle: 'rgba(0,0,0,0.1)', pointStyle: undefined, + rotation: undefined, datasetIndex: 1 }, { text: 'dataset3', fillStyle: 'rgba(0,0,0,0.1)', hidden: false, - lineCap: 'butt', - lineDash: [], - lineDashOffset: 0, - lineJoin: 'miter', + lineCap: undefined, + lineDash: undefined, + lineDashOffset: undefined, + lineJoin: undefined, lineWidth: 10, strokeStyle: 'green', - pointStyle: 'crossRot', + pointStyle: undefined, + rotation: undefined, datasetIndex: 2 }]); }); @@ -107,6 +110,7 @@ describe('Legend block tests', function() { borderWidth: 10, borderColor: 'green', pointStyle: 'crossRot', + fill: false, data: [] }], labels: [] @@ -124,6 +128,7 @@ describe('Legend block tests', function() { lineWidth: 3, strokeStyle: 'rgba(0,0,0,0.1)', pointStyle: undefined, + rotation: undefined, datasetIndex: 0 }, { text: 'dataset2', @@ -136,10 +141,11 @@ describe('Legend block tests', function() { lineWidth: 3, strokeStyle: 'rgba(0,0,0,0.1)', pointStyle: undefined, + rotation: undefined, datasetIndex: 1 }, { text: 'dataset3', - fillStyle: 'rgba(0,0,0,0.1)', + fillStyle: 'rgba(0,0,0,0)', hidden: false, lineCap: 'butt', lineDash: [], @@ -147,7 +153,8 @@ describe('Legend block tests', function() { lineJoin: 'miter', lineWidth: 10, strokeStyle: 'green', - pointStyle: 'crossRot', + pointStyle: undefined, + rotation: undefined, datasetIndex: 2 }]); }); @@ -159,14 +166,14 @@ describe('Legend block tests', function() { datasets: [{ label: 'dataset1', backgroundColor: '#f31', - borderCapStyle: 'round', + borderCapStyle: 'butt', borderDash: [2, 2], borderDashOffset: 5.5, data: [] }, { label: 'dataset2', hidden: true, - borderJoinStyle: 'round', + borderJoinStyle: 'miter', data: [], legendHidden: true }, { @@ -194,25 +201,27 @@ describe('Legend block tests', function() { text: 'dataset1', fillStyle: '#f31', hidden: false, - lineCap: 'butt', - lineDash: [], - lineDashOffset: 0, - lineJoin: 'miter', + lineCap: undefined, + lineDash: undefined, + lineDashOffset: undefined, + lineJoin: undefined, lineWidth: 0, strokeStyle: 'rgba(0,0,0,0.1)', pointStyle: undefined, + rotation: undefined, datasetIndex: 0 }, { text: 'dataset3', fillStyle: 'rgba(0,0,0,0.1)', hidden: false, - lineCap: 'butt', - lineDash: [], - lineDashOffset: 0, - lineJoin: 'miter', + lineCap: undefined, + lineDash: undefined, + lineDashOffset: undefined, + lineJoin: undefined, lineWidth: 10, strokeStyle: 'green', - pointStyle: 'crossRot', + pointStyle: undefined, + rotation: undefined, datasetIndex: 2 }]); }); @@ -225,7 +234,7 @@ describe('Legend block tests', function() { datasets: [{ label: 'dataset1', backgroundColor: '#f31', - borderCapStyle: 'round', + borderCapStyle: 'butt', borderDash: [2, 2], borderDashOffset: 5.5, data: [] @@ -249,14 +258,14 @@ describe('Legend block tests', function() { datasets: [{ label: 'dataset1', backgroundColor: '#f31', - borderCapStyle: 'round', + borderCapStyle: 'butt', borderDash: [2, 2], borderDashOffset: 5.5, data: [] }, { label: 'dataset2', hidden: true, - borderJoinStyle: 'round', + borderJoinStyle: 'miter', data: [] }, { label: 'dataset3', @@ -328,30 +337,33 @@ describe('Legend block tests', function() { "args": ["butt"] }, { "name": "setLineDashOffset", - "args": [0] + "args": [5.5] }, { "name": "setLineJoin", "args": ["miter"] }, { "name": "setLineWidth", - "args": [0] + "args": [3] }, { "name": "setStrokeStyle", "args": ["rgba(0,0,0,0.1)"] }, { "name": "setLineDash", "args": [ - [] + [2, 2] ] + }, { + "name": "strokeRect", + "args": [114, 110, 40, 12] }, { "name": "fillRect", - "args": [52, 10, 40, 12] + "args": [114, 110, 40, 12] }, { "name": "restore", "args": [] }, { "name": "fillText", - "args": ["dataset1", 98, 16] + "args": ["dataset1", 160, 110] }, { "name": "measureText", "args": ["dataset2"] @@ -372,7 +384,7 @@ describe('Legend block tests', function() { "args": ["miter"] }, { "name": "setLineWidth", - "args": [0] + "args": [3] }, { "name": "setStrokeStyle", "args": ["rgba(0,0,0,0.1)"] @@ -381,15 +393,18 @@ describe('Legend block tests', function() { "args": [ [] ] + }, { + "name": "strokeRect", + "args": [250, 110, 40, 12] }, { "name": "fillRect", - "args": [188, 10, 40, 12] + "args": [250, 110, 40, 12] }, { "name": "restore", "args": [] }, { "name": "fillText", - "args": ["dataset2", 234, 16] + "args": ["dataset2", 296, 110] }, { "name": "beginPath", "args": [] @@ -398,10 +413,10 @@ describe('Legend block tests', function() { "args": [2] }, { "name": "moveTo", - "args": [234, 16] + "args": [296, 116] }, { "name": "lineTo", - "args": [314, 16] + "args": [376, 116] }, { "name": "stroke", "args": [] @@ -434,18 +449,18 @@ describe('Legend block tests', function() { "args": [ [] ] - }, { - "name": "fillRect", - "args": [324, 10, 40, 12] }, { "name": "strokeRect", - "args": [324, 10, 40, 12] + "args": [182, 132, 40, 12] + }, { + "name": "fillRect", + "args": [182, 132, 40, 12] }, { "name": "restore", "args": [] }, { "name": "fillText", - "args": ["dataset3", 370, 16] + "args": ["dataset3", 228, 132] }]);*/ }); @@ -634,7 +649,6 @@ describe('Legend block tests', function() { backgroundColor: ['#f31', '#666', '#14e'], borderWidth: [5, 10, 15], borderColor: ['red', 'green', 'blue'], - pointStyle: ['crossRot', 'triangle', 'star'], data: [] }], labels: [] @@ -645,13 +659,55 @@ describe('Legend block tests', function() { text: 'dataset1', fillStyle: '#f31', hidden: false, - lineCap: 'butt', - lineDash: [], - lineDashOffset: 0, - lineJoin: 'miter', + lineCap: undefined, + lineDash: undefined, + lineDashOffset: undefined, + lineJoin: undefined, lineWidth: 5, strokeStyle: 'red', - pointStyle: 'crossRot', + pointStyle: undefined, + rotation: undefined, + datasetIndex: 0 + }]); + }); + + it('should use the value for the first item when the property is a function', function() { + var helpers = window.Chart.helpers; + var chart = window.acquireChart({ + type: 'bar', + data: { + datasets: [{ + label: 'dataset1', + backgroundColor: function(ctx) { + var value = ctx.dataset.data[ctx.dataIndex] || 0; + return helpers.color({r: value * 10, g: 0, b: 0}).rgbString(); + }, + borderWidth: function(ctx) { + var value = ctx.dataset.data[ctx.dataIndex] || 0; + return value; + }, + borderColor: function(ctx) { + var value = ctx.dataset.data[ctx.dataIndex] || 0; + return helpers.color({r: 255 - value * 10, g: 0, b: 0}).rgbString(); + }, + data: [5, 10, 15, 20] + }], + labels: ['A', 'B', 'C', 'D'] + } + }); + + expect(chart.legend.legendItems).toEqual([{ + text: 'dataset1', + fillStyle: 'rgb(50, 0, 0)', + hidden: false, + lineCap: undefined, + lineDash: undefined, + lineDashOffset: undefined, + lineJoin: undefined, + lineWidth: 5, + strokeStyle: 'rgb(205, 0, 0)', + pointStyle: undefined, + rotation: undefined, datasetIndex: 0 }]); }); @@ -663,11 +719,9 @@ describe('Legend block tests', function() { datasets: [{ label: 'dataset1', backgroundColor: '#f31', - hidden: false, - borderCapStyle: 'round', + borderCapStyle: 'butt', borderDash: [2, 2], borderDashOffset: 5.5, - borderJoinStyle: 'round', borderWidth: 0, borderColor: '#f31', pointStyle: 'crossRot', @@ -678,14 +732,11 @@ describe('Legend block tests', function() { }, { label: 'dataset2', backgroundColor: '#f31', - hidden: false, - borderCapStyle: 'round', - borderDash: [2, 2], - borderDashOffset: 5.5, - borderJoinStyle: 'round', - borderWidth: 0, + borderJoinStyle: 'miter', + borderWidth: 2, borderColor: '#f31', pointStyle: 'crossRot', + pointRotation: 15, data: [] }], labels: [] @@ -703,25 +754,27 @@ describe('Legend block tests', function() { text: 'dataset1', fillStyle: 'rgba(0,0,0,0.1)', hidden: false, - lineCap: 'butt', - lineDash: [], - lineDashOffset: 0, - lineJoin: 'miter', + lineCap: undefined, + lineDash: undefined, + lineDashOffset: undefined, + lineJoin: undefined, lineWidth: 5, strokeStyle: 'green', pointStyle: 'crossRot', + rotation: undefined, datasetIndex: 0 }, { text: 'dataset2', fillStyle: '#f31', hidden: false, - lineCap: 'butt', - lineDash: [], - lineDashOffset: 0, - lineJoin: 'miter', - lineWidth: 0, + lineCap: undefined, + lineDash: undefined, + lineDashOffset: undefined, + lineJoin: undefined, + lineWidth: 2, strokeStyle: '#f31', pointStyle: 'crossRot', + rotation: 15, datasetIndex: 1 }]); });