*/
_handleEvent(e, replay) {
const me = this;
- const lastActive = me._active || [];
- const options = me.options;
+ const {_active: lastActive = [], options} = me;
const hoverOptions = options.hover;
// If the event is replayed from `update`, we should evaluate with the final positions.
let active = [];
let changed = false;
+ let lastEvent = null;
// Find Active Elements for hover and tooltips
- if (e.type === 'mouseout') {
- me._lastEvent = null;
- } else {
+ if (e.type !== 'mouseout') {
active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions, useFinalPosition);
- me._lastEvent = e.type === 'click' ? me._lastEvent : e;
+ lastEvent = e.type === 'click' ? me._lastEvent : e;
}
+ // Set _lastEvent to null while we are processing the event handlers.
+ // This prevents recursion if the handler calls chart.update()
+ me._lastEvent = null;
// Invoke onHover hook
callCallback(options.onHover || hoverOptions.onHover, [e, active, me], me);
me._updateHoverStyles(active, lastActive, replay);
}
+ me._lastEvent = lastEvent;
+
return changed;
}
}
return tooltipItems;
}
- update(changed) {
+ update(changed, replay) {
const me = this;
const options = me.options.setContext(me.getContext());
const active = me._active;
}
if (changed && options.external) {
- options.external.call(me, {chart: me._chart, tooltip: me});
+ options.external.call(me, {chart: me._chart, tooltip: me, replay});
}
}
y: e.y
};
- me.update(true);
+ me.update(true, replay);
}
}
--- /dev/null
+function drawMousePoint(ctx, center) {
+ ctx.beginPath();
+ ctx.arc(center.x, center.y, 8, 0, Math.PI * 2);
+ ctx.fillStyle = 'yellow';
+ ctx.fill();
+}
+
+const canvas = document.createElement('canvas');
+canvas.width = 512;
+canvas.height = 512;
+const ctx = canvas.getContext('2d');
+
+module.exports = {
+ config: {
+ type: 'pie',
+ data: {
+ datasets: [{
+ backgroundColor: ['red', 'green', 'blue'],
+ hoverBackgroundColor: 'black',
+ data: [1, 1, 1]
+ }]
+ }
+ },
+ options: {
+ canvas: {
+ width: 512,
+ height: 512
+ },
+ async run(chart) {
+ ctx.drawImage(chart.canvas, 0, 0, 256, 256);
+
+ const arc = chart.getDatasetMeta(0).data[0];
+ const center = arc.getCenterPoint();
+ await jasmine.triggerMouseEvent(chart, 'mousemove', arc);
+ drawMousePoint(chart.ctx, center);
+ ctx.drawImage(chart.canvas, 256, 0, 256, 256);
+
+ chart.toggleDataVisibility(0);
+ chart.update();
+ drawMousePoint(chart.ctx, center);
+ ctx.drawImage(chart.canvas, 0, 256, 256, 256);
+
+ await jasmine.triggerMouseEvent(chart, 'mouseout', arc);
+ ctx.drawImage(chart.canvas, 256, 256, 256, 256);
+
+ Chart.helpers.clearCanvas(chart.canvas);
+ chart.ctx.drawImage(canvas, 0, 0);
+ }
+ }
+};
// First dispatch change event, should update tooltip
await jasmine.triggerMouseEvent(chart, 'mousemove', firstPoint);
- expect(tooltip.update).toHaveBeenCalledWith(true);
+ expect(tooltip.update).toHaveBeenCalledWith(true, undefined);
// Reset calls
tooltip.update.calls.reset();
// First dispatch change event, should update tooltip
await jasmine.triggerMouseEvent(chart, 'mousemove', firstPoint);
- expect(tooltip.update).toHaveBeenCalledWith(true);
+ expect(tooltip.update).toHaveBeenCalledWith(true, undefined);
// Reset calls
tooltip.update.calls.reset();
// Second dispatch change event (same event), should update tooltip
// because position mode is 'nearest'
await jasmine.triggerMouseEvent(chart, 'mousemove', secondPoint);
- expect(tooltip.update).toHaveBeenCalledWith(true);
+ expect(tooltip.update).toHaveBeenCalledWith(true, undefined);
});
describe('positioners', function() {