diff --git a/docs/docs/configuration/tooltip.md b/docs/docs/configuration/tooltip.md
index 56f1bbe6771..aef329af802 100644
--- a/docs/docs/configuration/tooltip.md
+++ b/docs/docs/configuration/tooltip.md
@@ -37,6 +37,7 @@ The tooltip configuration is passed into the `options.tooltips` namespace. The g
| `displayColors` | `boolean` | `true` | If true, color boxes are shown in the tooltip.
| `boxWidth` | `number` | `bodyFont.size` | Width of the color box if displayColors is true.
| `boxHeight` | `number` | `bodyFont.size` | Height of the color box if displayColors is true.
+| `usePointStyle` | `boolean` | `false` | Use the corresponding point style (from dataset options) instead of color boxes, ex: star, triangle etc. (size is based on the minimum value between boxWidth and boxHeight).
| `borderColor` | `Color` | `'rgba(0, 0, 0, 0)'` | Color of the border.
| `borderWidth` | `number` | `0` | Size of the border.
| `rtl` | `boolean` | | `true` for rendering the legends from right to left.
@@ -111,6 +112,7 @@ All functions are called with the same arguments: a [tooltip item context](#tool
| `label` | `TooltipItem, object` | Returns text to render for an individual item in the tooltip. [more...](#label-callback)
| `labelColor` | `TooltipItem, Chart` | Returns the colors to render for the tooltip item. [more...](#label-color-callback)
| `labelTextColor` | `TooltipItem, Chart` | Returns the colors for the text of the label for the tooltip item.
+| `labelPointStyle` | `TooltipItem, Chart` | Returns the point style to use instead of color boxes if usePointStyle is true (object with values `pointStyle` and `rotation`). Default implementation uses the point style from the dataset points. [more...](#label-point-style-callback)
| `afterLabel` | `TooltipItem, object` | Returns text to render after an individual label.
| `afterBody` | `TooltipItem[], object` | Returns text to render after the body section.
| `beforeFooter` | `TooltipItem[], object` | Returns text to render before the footer section.
@@ -171,6 +173,30 @@ var chart = new Chart(ctx, {
});
```
+### Label Point Style Callback
+
+For example, to draw triangles instead of the regular color box for each item in the tooltip you could do:
+
+```javascript
+var chart = new Chart(ctx, {
+ type: 'line',
+ data: data,
+ options: {
+ tooltips: {
+ usePointStyle: true,
+ callbacks: {
+ labelPointStyle: function(context) {
+ return {
+ pointStyle: 'triangle',
+ rotation: 0
+ };
+ }
+ }
+ }
+ }
+});
+```
+
### Tooltip Item Context
diff --git a/samples/samples.js b/samples/samples.js
index eb569df52e7..a5025f1f09b 100644
--- a/samples/samples.js
+++ b/samples/samples.js
@@ -205,6 +205,9 @@
}, {
title: 'Border',
path: 'tooltips/border.html'
+ }, {
+ title: 'Point style',
+ path: 'tooltips/point-style.html'
}, {
title: 'HTML tooltips (line)',
path: 'tooltips/custom-line.html'
diff --git a/samples/tooltips/point-style.html b/samples/tooltips/point-style.html
new file mode 100644
index 00000000000..6ae1376c4d0
--- /dev/null
+++ b/samples/tooltips/point-style.html
@@ -0,0 +1,193 @@
+
+
+
+
+ Tooltip Point Style
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/plugins/plugin.tooltip.js b/src/plugins/plugin.tooltip.js
index d9da0d8098e..eac3e5123b5 100644
--- a/src/plugins/plugin.tooltip.js
+++ b/src/plugins/plugin.tooltip.js
@@ -5,6 +5,7 @@ import {valueOrDefault, each, noop, isNullOrUndef, isArray, _elementsEqual, merg
import {getRtlAdapter, overrideTextDirection, restoreTextDirection} from '../helpers/helpers.rtl';
import {distanceBetweenPoints} from '../helpers/helpers.math';
import {toFont} from '../helpers/helpers.options';
+import {drawPoint} from '../helpers';
/**
* @typedef { import("../platform/platform.base").IEvent } IEvent
@@ -382,6 +383,7 @@ export class Tooltip extends Element {
this.caretX = undefined;
this.caretY = undefined;
this.labelColors = undefined;
+ this.labelPointStyles = undefined;
this.labelTextColors = undefined;
this.initialize();
@@ -485,6 +487,7 @@ export class Tooltip extends Element {
const options = me.options;
const data = me._chart.data;
const labelColors = [];
+ const labelPointStyles = [];
const labelTextColors = [];
let tooltipItems = [];
let i, len;
@@ -506,10 +509,12 @@ export class Tooltip extends Element {
// Determine colors for boxes
each(tooltipItems, (context) => {
labelColors.push(options.callbacks.labelColor.call(me, context));
+ labelPointStyles.push(options.callbacks.labelPointStyle.call(me, context));
labelTextColors.push(options.callbacks.labelTextColor.call(me, context));
});
me.labelColors = labelColors;
+ me.labelPointStyles = labelPointStyles;
me.labelTextColors = labelTextColors;
me.dataPoints = tooltipItems;
return tooltipItems;
@@ -668,24 +673,48 @@ export class Tooltip extends Element {
const me = this;
const options = me.options;
const labelColors = me.labelColors[i];
+ const labelPointStyle = me.labelPointStyles[i];
const {boxHeight, boxWidth, bodyFont} = options;
const colorX = getAlignedX(me, 'left');
const rtlColorX = rtlHelper.x(colorX);
const yOffSet = boxHeight < bodyFont.size ? (bodyFont.size - boxHeight) / 2 : 0;
const colorY = pt.y + yOffSet;
- // Fill a white rect so that colours merge nicely if the opacity is < 1
- ctx.fillStyle = options.multiKeyBackground;
- ctx.fillRect(rtlHelper.leftForLtr(rtlColorX, boxWidth), colorY, boxWidth, boxHeight);
-
- // Border
- ctx.lineWidth = 1;
- ctx.strokeStyle = labelColors.borderColor;
- ctx.strokeRect(rtlHelper.leftForLtr(rtlColorX, boxWidth), colorY, boxWidth, boxHeight);
-
- // Inner square
- ctx.fillStyle = labelColors.backgroundColor;
- ctx.fillRect(rtlHelper.leftForLtr(rtlHelper.xPlus(rtlColorX, 1), boxWidth - 2), colorY + 1, boxWidth - 2, boxHeight - 2);
+ if (options.usePointStyle) {
+ const drawOptions = {
+ radius: Math.min(boxWidth, boxHeight) / 2, // fit the circle in the box
+ pointStyle: labelPointStyle.pointStyle,
+ rotation: labelPointStyle.rotation,
+ borderWidth: 1
+ };
+ // Recalculate x and y for drawPoint() because its expecting
+ // x and y to be center of figure (instead of top left)
+ const centerX = rtlHelper.leftForLtr(rtlColorX, boxWidth) + boxWidth / 2;
+ const centerY = colorY + boxHeight / 2;
+
+ // Fill the point with white so that colours merge nicely if the opacity is < 1
+ ctx.strokeStyle = options.multiKeyBackground;
+ ctx.fillStyle = options.multiKeyBackground;
+ drawPoint(ctx, drawOptions, centerX, centerY);
+
+ // Draw the point
+ ctx.strokeStyle = labelColors.borderColor;
+ ctx.fillStyle = labelColors.backgroundColor;
+ drawPoint(ctx, drawOptions, centerX, centerY);
+ } else {
+ // Fill a white rect so that colours merge nicely if the opacity is < 1
+ ctx.fillStyle = options.multiKeyBackground;
+ ctx.fillRect(rtlHelper.leftForLtr(rtlColorX, boxWidth), colorY, boxWidth, boxHeight);
+
+ // Border
+ ctx.lineWidth = 1;
+ ctx.strokeStyle = labelColors.borderColor;
+ ctx.strokeRect(rtlHelper.leftForLtr(rtlColorX, boxWidth), colorY, boxWidth, boxHeight);
+
+ // Inner square
+ ctx.fillStyle = labelColors.backgroundColor;
+ ctx.fillRect(rtlHelper.leftForLtr(rtlHelper.xPlus(rtlColorX, 1), boxWidth - 2), colorY + 1, boxWidth - 2, boxHeight - 2);
+ }
// restore fillStyle
ctx.fillStyle = me.labelTextColors[i];
@@ -1155,6 +1184,14 @@ export default {
labelTextColor() {
return this.options.bodyFont.color;
},
+ labelPointStyle(tooltipItem) {
+ const meta = tooltipItem.chart.getDatasetMeta(tooltipItem.datasetIndex);
+ const options = meta.controller.getStyle(tooltipItem.dataIndex);
+ return {
+ pointStyle: options.pointStyle,
+ rotation: options.rotation,
+ };
+ },
afterLabel: noop,
// Args are: (tooltipItems, data)
diff --git a/test/fixtures/core.tooltip/point-style.js b/test/fixtures/core.tooltip/point-style.js
new file mode 100644
index 00000000000..2579f395948
--- /dev/null
+++ b/test/fixtures/core.tooltip/point-style.js
@@ -0,0 +1,73 @@
+const pointStyles = ['circle', 'cross', 'crossRot', 'dash', 'line', 'rect', 'rectRounded', 'rectRot', 'star', 'triangle'];
+
+function newDataset(pointStyle, i) {
+ return {
+ label: '',
+ data: pointStyles.map(() => i),
+ pointStyle: pointStyle,
+ pointBackgroundColor: '#0000ff',
+ pointBorderColor: '#00ff00',
+ showLine: false
+ };
+}
+module.exports = {
+ config: {
+ type: 'line',
+ data: {
+ datasets: pointStyles.map((pointStyle, i) => newDataset(pointStyle, i)),
+ labels: pointStyles.map(() => '')
+ },
+ options: {
+ legend: false,
+ title: false,
+ scales: {
+ x: {display: false},
+ y: {display: false}
+ },
+ elements: {
+ line: {
+ fill: false
+ }
+ },
+ tooltips: {
+ mode: 'nearest',
+ intersect: false,
+ usePointStyle: true,
+ callbacks: {
+ label: function() {
+ return '\u200b';
+ }
+ }
+ },
+ layout: {
+ padding: 15
+ }
+ },
+ plugins: [{
+ afterDraw: function(chart) {
+ var canvas = chart.canvas;
+ var rect = canvas.getBoundingClientRect();
+ var point, event;
+
+ for (var i = 0; i < pointStyles.length; ++i) {
+ point = chart.getDatasetMeta(i).data[i];
+ event = {
+ type: 'mousemove',
+ target: canvas,
+ clientX: rect.left + point.x,
+ clientY: rect.top + point.y
+ };
+ chart._handleEvent(event);
+ chart.tooltip.handleEvent(event);
+ chart.tooltip.draw(chart.ctx);
+ }
+ }
+ }]
+ },
+ options: {
+ canvas: {
+ height: 256,
+ width: 512
+ }
+ }
+};
diff --git a/test/fixtures/core.tooltip/point-style.png b/test/fixtures/core.tooltip/point-style.png
new file mode 100644
index 00000000000..defb03359fc
Binary files /dev/null and b/test/fixtures/core.tooltip/point-style.png differ
diff --git a/test/specs/plugin.tooltip.tests.js b/test/specs/plugin.tooltip.tests.js
index 2e5f3cf8ea5..b3ba80fc1b3 100644
--- a/test/specs/plugin.tooltip.tests.js
+++ b/test/specs/plugin.tooltip.tests.js
@@ -369,6 +369,12 @@ describe('Plugin.Tooltip', function() {
},
labelTextColor: function() {
return 'labelTextColor';
+ },
+ labelPointStyle: function() {
+ return {
+ pointStyle: 'labelPointStyle',
+ rotation: 42
+ };
}
}
}
@@ -459,6 +465,13 @@ describe('Plugin.Tooltip', function() {
}, {
borderColor: defaults.color,
backgroundColor: defaults.color
+ }],
+ labelPointStyles: [{
+ pointStyle: 'labelPointStyle',
+ rotation: 42
+ }, {
+ pointStyle: 'labelPointStyle',
+ rotation: 42
}]
}));
diff --git a/types/plugins/index.d.ts b/types/plugins/index.d.ts
index e5855fe880f..514ff06a8de 100644
--- a/types/plugins/index.d.ts
+++ b/types/plugins/index.d.ts
@@ -281,6 +281,7 @@ export interface TooltipModel {
// colors to render for each item in body[]. This is the color of the squares in the tooltip
labelColors: Color[];
labelTextColors: Color[];
+ labelPointStyles: { pointStyle: PointStyle; rotation: number }[];
// 0 opacity is a hidden tooltip
opacity: number;
@@ -312,6 +313,7 @@ export interface ITooltipCallbacks {
labelColor(this: TooltipModel, tooltipItem: ITooltipItem): { borderColor: Color; backgroundColor: Color };
labelTextColor(this: TooltipModel, tooltipItem: ITooltipItem): Color;
+ labelPointStyle(this: TooltipModel, tooltipItem: ITooltipItem): { pointStyle: PointStyle; rotation: number };
beforeFooter(this: TooltipModel, tooltipItems: ITooltipItem[]): string | string[];
footer(this: TooltipModel, tooltipItems: ITooltipItem[]): string | string[];
@@ -473,6 +475,11 @@ export interface ITooltipOptions extends IHoverInteractionOptions {
* @default bodyFont.size
*/
boxHeight: number;
+ /**
+ * Use the corresponding point style (from dataset options) instead of color boxes, ex: star, triangle etc. (size is based on the minimum value between boxWidth and boxHeight)
+ * @default false
+ */
+ usePointStyle: boolean;
/**
* Color of the border.
* @default 'rgba(0, 0, 0, 0)'