mirror of
https://github.com/chartjs/Chart.js.git
synced 2025-12-08 20:36:08 +00:00
Provide APIs to set active (hovered) and tooltip elements. (#7845)
Provide APIs to set active (hovered) and tooltip elements. Chart.setActiveElements will set the hovered items. Chart.tooltip.setActiveElements will set the tooltip items.
This commit is contained in:
parent
8d36927b29
commit
a8a83d12cd
@ -205,6 +205,16 @@ Sets the visibility for the given dataset to true. Updates the chart and animate
|
||||
chart.show(1); // shows dataset at index 1 and does 'show' animation.
|
||||
```
|
||||
|
||||
## setActiveElements(activeElements)
|
||||
|
||||
Sets the active (hovered) elements for the chart. See the "Programmatic Events" sample file to see this in action.
|
||||
|
||||
```javascript
|
||||
chart.setActiveElements([
|
||||
{datasetIndex: 0, index: 1},
|
||||
]);
|
||||
```
|
||||
|
||||
## Static: getChart(key)
|
||||
|
||||
Finds the chart instance from the given key. If the key is a `string`, it is interpreted as the ID of the Canvas node for the Chart. The key can also be a `CanvasRenderingContext2D` or an `HTMLDOMElement`. This will return `undefined` if no Chart is found. To be found, the chart must have previously been created.
|
||||
|
||||
120
samples/advanced/programmatic-events.html
Normal file
120
samples/advanced/programmatic-events.html
Normal file
@ -0,0 +1,120 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Programmatic Event Triggers</title>
|
||||
<script src="../../dist/chart.js"></script>
|
||||
<script src="../utils.js"></script>
|
||||
<style>
|
||||
canvas {
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="container" style="width: 75%;">
|
||||
<canvas id="canvas"></canvas>
|
||||
</div>
|
||||
<button id="hover">Trigger Hover</button>
|
||||
<button id="tooltip">Trigger Tooltip</button>
|
||||
<script>
|
||||
var color = Chart.helpers.color;
|
||||
var barChartData = {
|
||||
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
|
||||
datasets: [{
|
||||
label: 'Dataset 1',
|
||||
backgroundColor: color(window.chartColors.red).alpha(0.5).rgbString(),
|
||||
borderColor: window.chartColors.red,
|
||||
borderWidth: 1,
|
||||
hoverBorderWidth: 5,
|
||||
hoverBorderColor: 'green',
|
||||
data: [
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor()
|
||||
]
|
||||
}, {
|
||||
label: 'Dataset 2',
|
||||
backgroundColor: color(window.chartColors.blue).alpha(0.5).rgbString(),
|
||||
borderColor: window.chartColors.blue,
|
||||
borderWidth: 1,
|
||||
hoverBorderWidth: 5,
|
||||
hoverBorderColor: 'green',
|
||||
data: [
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor(),
|
||||
randomScalingFactor()
|
||||
]
|
||||
}]
|
||||
|
||||
};
|
||||
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById('canvas').getContext('2d');
|
||||
window.myBar = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: barChartData,
|
||||
options: {
|
||||
responsive: true,
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
document.getElementById('hover').addEventListener('click', function() {
|
||||
if (window.myBar.getActiveElements().length > 0) {
|
||||
window.myBar.setActiveElements([]);
|
||||
} else {
|
||||
window.myBar.setActiveElements(
|
||||
[
|
||||
{
|
||||
datasetIndex: 0,
|
||||
index: 0,
|
||||
}, {
|
||||
datasetIndex: 1,
|
||||
index: 0,
|
||||
}
|
||||
]);
|
||||
}
|
||||
window.myBar.update();
|
||||
});
|
||||
|
||||
document.getElementById('tooltip').addEventListener('click', function() {
|
||||
const tooltip = window.myBar.tooltip;
|
||||
if (tooltip.getActiveElements().length > 0) {
|
||||
tooltip.setActiveElements([], {x: 0, y: 0});
|
||||
} else {
|
||||
const chartArea = window.myBar.chartArea;
|
||||
tooltip.setActiveElements(
|
||||
[
|
||||
{
|
||||
datasetIndex: 0,
|
||||
index: 2,
|
||||
}, {
|
||||
datasetIndex: 1,
|
||||
index: 2,
|
||||
}
|
||||
],
|
||||
{
|
||||
x: (chartArea.left + chartArea.right) / 2,
|
||||
y: (chartArea.top + chartArea.bottom) / 2,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
window.myBar.update();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -250,6 +250,9 @@
|
||||
}, {
|
||||
title: 'Line Gradient',
|
||||
path: 'advanced/line-gradient.html'
|
||||
}, {
|
||||
title: 'Programmatic Event Triggers',
|
||||
path: 'advanced/programmatic-events.html'
|
||||
}]
|
||||
}];
|
||||
|
||||
|
||||
@ -1061,6 +1061,41 @@ class Chart {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get active (hovered) elements
|
||||
* @returns array
|
||||
*/
|
||||
getActiveElements() {
|
||||
return this._active || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set active (hovered) elements
|
||||
* @param {array} activeElements New active data points
|
||||
*/
|
||||
setActiveElements(activeElements) {
|
||||
const me = this;
|
||||
const lastActive = me._active || [];
|
||||
const active = activeElements.map(({datasetIndex, index}) => {
|
||||
const meta = me.getDatasetMeta(datasetIndex);
|
||||
if (!meta) {
|
||||
throw new Error('No dataset found at index ' + datasetIndex);
|
||||
}
|
||||
|
||||
return {
|
||||
datasetIndex,
|
||||
element: meta.data[index],
|
||||
index,
|
||||
};
|
||||
});
|
||||
const changed = !_elementsEqual(active, lastActive);
|
||||
|
||||
if (changed) {
|
||||
me._active = active;
|
||||
me._updateHoverStyles(active, lastActive);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
||||
@ -908,6 +908,45 @@ export class Tooltip extends Element {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get active elements in the tooltip
|
||||
* @returns {Array} Array of elements that are active in the tooltip
|
||||
*/
|
||||
getActiveElements() {
|
||||
return this._active || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set active elements in the tooltip
|
||||
* @param {array} activeElements Array of active datasetIndex/index pairs.
|
||||
* @param {object} eventPosition Synthetic event position used in positioning
|
||||
*/
|
||||
setActiveElements(activeElements, eventPosition) {
|
||||
const me = this;
|
||||
const lastActive = me._active;
|
||||
const active = activeElements.map(({datasetIndex, index}) => {
|
||||
const meta = me._chart.getDatasetMeta(datasetIndex);
|
||||
|
||||
if (!meta) {
|
||||
throw new Error('Cannot find a dataset at index ' + datasetIndex);
|
||||
}
|
||||
|
||||
return {
|
||||
datasetIndex,
|
||||
element: meta.data[index],
|
||||
index,
|
||||
};
|
||||
});
|
||||
const changed = !_elementsEqual(lastActive, active);
|
||||
const positionChanged = me._positionChanged(active, eventPosition);
|
||||
|
||||
if (changed || positionChanged) {
|
||||
me._active = active;
|
||||
me._eventPosition = eventPosition;
|
||||
me.update(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an event
|
||||
* @param {IEvent} e - The event to handle
|
||||
@ -932,8 +971,7 @@ export class Tooltip extends Element {
|
||||
// When there are multiple items shown, but the tooltip position is nearest mode
|
||||
// an update may need to be made because our position may have changed even though
|
||||
// the items are the same as before.
|
||||
const position = positioners[options.position].call(me, active, e);
|
||||
const positionChanged = this.caretX !== position.x || this.caretY !== position.y;
|
||||
const positionChanged = me._positionChanged(active, e);
|
||||
|
||||
// Remember Last Actives
|
||||
changed = replay || !_elementsEqual(active, lastActive) || positionChanged;
|
||||
@ -954,6 +992,19 @@ export class Tooltip extends Element {
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the active elements + event combination changes the
|
||||
* tooltip position
|
||||
* @param {array} active - Active elements
|
||||
* @param {IEvent} e - Event that triggered the position change
|
||||
* @returns {boolean} True if the position has changed
|
||||
*/
|
||||
_positionChanged(active, e) {
|
||||
const me = this;
|
||||
const position = positioners[me.options.position].call(me, active, e);
|
||||
return me.caretX !== position.x || me.caretY !== position.y;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1511,4 +1511,35 @@ describe('Chart', function() {
|
||||
expect(Chart.getChart(1)).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('active elements', function() {
|
||||
it('should set the active elements', function() {
|
||||
var chart = acquireChart({
|
||||
type: 'pie',
|
||||
data: {
|
||||
datasets: [{
|
||||
data: [1, 2, 3],
|
||||
borderColor: 'red',
|
||||
hoverBorderColor: 'blue',
|
||||
}]
|
||||
}
|
||||
});
|
||||
|
||||
const meta = chart.getDatasetMeta(0);
|
||||
let props = meta.data[0].getProps(['borderColor']);
|
||||
expect(props.options.borderColor).toEqual('red');
|
||||
|
||||
chart.setActiveElements([{
|
||||
datasetIndex: 0,
|
||||
index: 0,
|
||||
}]);
|
||||
|
||||
props = meta.data[0].getProps(['borderColor']);
|
||||
expect(props.options.borderColor).toEqual('blue');
|
||||
|
||||
const active = chart.getActiveElements();
|
||||
expect(active.length).toEqual(1);
|
||||
expect(active[0].element).toBe(meta.data[0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1490,4 +1490,25 @@ describe('Plugin.Tooltip', function() {
|
||||
]));
|
||||
});
|
||||
});
|
||||
|
||||
describe('active events', function() {
|
||||
it('should set the active events', function() {
|
||||
var chart = window.acquireChart({
|
||||
type: 'line',
|
||||
data: {
|
||||
datasets: [{
|
||||
label: 'Dataset 1',
|
||||
data: [10, 20, 30],
|
||||
pointHoverBorderColor: 'rgb(255, 0, 0)',
|
||||
pointHoverBackgroundColor: 'rgb(0, 255, 0)'
|
||||
}],
|
||||
labels: ['Point 1', 'Point 2', 'Point 3']
|
||||
},
|
||||
});
|
||||
|
||||
const meta = chart.getDatasetMeta(0);
|
||||
chart.tooltip.setActiveElements([{datasetIndex: 0, index: 0}], {x: 0, y: 0});
|
||||
expect(chart.tooltip.getActiveElements()[0].element).toBe(meta.data[0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
12
types/core/index.d.ts
vendored
12
types/core/index.d.ts
vendored
@ -240,6 +240,15 @@ export interface IParsingOptions {
|
||||
| false;
|
||||
}
|
||||
|
||||
export interface ActiveDataPoint {
|
||||
datasetIndex: number;
|
||||
index: number;
|
||||
}
|
||||
|
||||
export interface ActiveElement extends ActiveDataPoint {
|
||||
element: Element;
|
||||
}
|
||||
|
||||
export declare class Chart<
|
||||
TYPE extends IChartType = IChartType,
|
||||
DATA extends unknown[] = DefaultDataPoint<TYPE>,
|
||||
@ -293,6 +302,9 @@ export declare class Chart<
|
||||
hide(datasetIndex: number): void;
|
||||
show(datasetIndex: number): void;
|
||||
|
||||
getActiveElements(): ActiveElement[];
|
||||
setActiveElements(active: ActiveDataPoint[]);
|
||||
|
||||
destroy(): void;
|
||||
toBase64Image(type?: string, quality?: any): string;
|
||||
bindEvents(): void;
|
||||
|
||||
5
types/plugins/index.d.ts
vendored
5
types/plugins/index.d.ts
vendored
@ -1,4 +1,4 @@
|
||||
import { Chart, Element, IAnimationSpecContainer, InteractionMode, LayoutPosition, IPlugin } from '../core';
|
||||
import { ActiveDataPoint, ActiveElement, Chart, Element, IAnimationSpecContainer, InteractionMode, LayoutPosition, IPlugin } from '../core';
|
||||
import { Color, IChartArea, IFontSpec, Scriptable, TextAlign, IEvent, IHoverInteractionOptions } from '../core/interfaces';
|
||||
import { PointStyle } from '../elements';
|
||||
import { IChartData, IChartDataset } from '../interfaces';
|
||||
@ -281,6 +281,9 @@ export const Tooltip: IPlugin & {
|
||||
readonly positioners: {
|
||||
[key: string]: (items: readonly Element[], eventPosition: { x: number; y: number }) => { x: number; y: number };
|
||||
};
|
||||
|
||||
getActiveElements(): ActiveElement[];
|
||||
setActiveElements(active: ActiveDataPoint[], eventPosition: { x: number, y: number });
|
||||
};
|
||||
|
||||
export interface ITooltipCallbacks {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user