diff --git a/docs/configuration/legend.md b/docs/configuration/legend.md index 1621f5a87d1..7da82739e9b 100644 --- a/docs/configuration/legend.md +++ b/docs/configuration/legend.md @@ -71,6 +71,7 @@ Namespace: `options.plugins.legend.labels` | `textAlign` | `string` | `'center'` | Horizontal alignment of the label text. Options are: `'left'`, `'right'` or `'center'`. | `usePointStyle` | `boolean` | `false` | Label style will match corresponding point style (size is based on pointStyleWidth or the minimum value between boxWidth and font.size). | `pointStyleWidth` | `number` | `null` | If `usePointStyle` is true, the width of the point style used for the legend. +| `pointStyleYOffset` | `number` | `0` | If `usePointStyle` is true, the vertical offset in pixels applied to the point style drawn for the legend item. Useful for visually centering asymmetric point styles such as `line` or `dash`, or for problematic fonts. | `useBorderRadius` | `boolean` | `false` | Label borderRadius will match corresponding borderRadius. | `borderRadius` | `number` | `undefined` | Override the borderRadius to use. diff --git a/docs/samples/legend/point-style.md b/docs/samples/legend/point-style.md index 355045c676e..4b720d827c3 100644 --- a/docs/samples/legend/point-style.md +++ b/docs/samples/legend/point-style.md @@ -58,12 +58,15 @@ module.exports = { }; ``` -## Docs +For asymmetric point styles such as `line` or `dash`, you can nudge the symbol vertically using the `pointStyleYOffset` option set to a small positive number (e.g., `4` pixels). + +## Docs * [Data structures (`labels`)](../../general/data-structures.md) * [Line](../../charts/line.md) * [Legend](../../configuration/legend.md) - * [Legend Label Configuration](../../configuration/legend.md#legend-label-configuration) - * `usePointStyle` + * [Legend Label Configuration](../../configuration/legend.md#legend-label-configuration) + * `usePointStyle` + * `pointStyleYOffset` * [Elements](../../configuration/elements.md) - * [Point Configuration](../../configuration/elements.md#point-configuration) - * [Point Styles](../../configuration/elements.md#point-styles) + * [Point Configuration](../../configuration/elements.md#point-configuration) + * [Point Styles](../../configuration/elements.md#point-styles) diff --git a/src/plugins/plugin.legend.js b/src/plugins/plugin.legend.js index 6ed99413536..bb77a2df545 100644 --- a/src/plugins/plugin.legend.js +++ b/src/plugins/plugin.legend.js @@ -417,7 +417,7 @@ export class Legend extends Element { const realX = rtlHelper.x(x); - drawLegendBox(realX, y, legendItem); + drawLegendBox(realX, y + (labelOpts.pointStyleYOffset || 0), legendItem); x = _textX(textAlign, x + boxWidth + halfFontSize, isHorizontal ? x + width : this.right, opts.rtl); diff --git a/src/types/index.d.ts b/src/types/index.d.ts index 32831adc88c..1479cd0db9f 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -2496,6 +2496,10 @@ export interface LegendOptions { * If usePointStyle is true, the width of the point style used for the legend. */ pointStyleWidth: number; + /** + * If you are using a font that causes incorrect alignment, adjust this value to ensure proper alignment. + */ + datasetRadiusBuffer: number; /** * Generates legend items for each thing in the legend. Default implementation returns the text + styling for the color box. See Legend Item for details. */ diff --git a/test/fixtures/plugin.legend/pointStyle-y-offset/legend-pointStyle-y-offset.json b/test/fixtures/plugin.legend/pointStyle-y-offset/legend-pointStyle-y-offset.json new file mode 100644 index 00000000000..f68b3b8bafa --- /dev/null +++ b/test/fixtures/plugin.legend/pointStyle-y-offset/legend-pointStyle-y-offset.json @@ -0,0 +1,53 @@ +{ + "config": { + "type": "line", + "data": { + "labels": ["A"], + "datasets": [ + { + "label": "Line", + "data": [10], + "backgroundColor": "#4bc0c0", + "borderColor": "#4bc0c0", + "borderWidth": 2, + "pointStyle": "line" + }, + { + "label": "Dash", + "data": [20], + "backgroundColor": "#ff6384", + "borderColor": "#ff6384", + "borderWidth": 2, + "pointStyle": "dash" + } + ] + }, + "options": { + "plugins": { + "legend": { + "display": true, + "labels": { + "usePointStyle": true, + "pointStyleWidth": 30, + "pointStyleYOffset": 4 + } + } + }, + "scales": { + "x": { + "display": false + }, + "y": { + "display": false + } + } + } + }, + "options": { + "canvas": { + "width": 512, + "height": 128 + } + } +} + diff --git a/test/fixtures/plugin.legend/pointStyle-y-offset/legend-pointStyle-y-offset.png b/test/fixtures/plugin.legend/pointStyle-y-offset/legend-pointStyle-y-offset.png new file mode 100644 index 00000000000..f66bc5884b3 Binary files /dev/null and b/test/fixtures/plugin.legend/pointStyle-y-offset/legend-pointStyle-y-offset.png differ diff --git a/test/specs/plugin.legend.tests.js b/test/specs/plugin.legend.tests.js index e0bed42c263..df785174a04 100644 --- a/test/specs/plugin.legend.tests.js +++ b/test/specs/plugin.legend.tests.js @@ -877,6 +877,55 @@ describe('Legend block tests', function() { }]); }); + it('should apply pointStyleYOffset to the legend point symbol only', function() { + function drawWithOffset(offset) { + var chart = window.acquireChart({ + type: 'line', + data: { + labels: ['A'], + datasets: [{ + label: 'dataset1', + data: [1], + pointStyle: 'circle' + }] + }, + options: { + plugins: { + legend: { + labels: { + usePointStyle: true, + pointStyleYOffset: offset + } + } + } + } + }); + + var mockContext = window.createMockContext(); + chart.legend.ctx = mockContext; + chart.legend.draw(); + + var calls = mockContext.getCalls(); + var arcCall = calls.filter(function(call) { + return call.name === 'arc'; + })[0]; + var fillTextCall = calls.filter(function(call) { + return call.name === 'fillText'; + })[0]; + + return { + arcY: arcCall.args[1], + textY: fillTextCall.args[2] + }; + } + + var base = drawWithOffset(0); + var shifted = drawWithOffset(6); + + expect(shifted.arcY - base.arcY).toBeCloseTo(6, 6); + expect(shifted.textY).toBeCloseTo(base.textY, 6); + }); + it('should not crash when the legend defaults are false', function() { const oldDefaults = Chart.defaults.plugins.legend;