--- /dev/null
+<!doctype html>
+<html>
+<head>
+ <title>Legend Callbacks</title>
+ <script src="../../dist/Chart.min.js"></script>
+ <script src="../utils.js"></script>
+ <style>
+ body, html {
+ height: 100%;
+ font-family: sans-serif;
+ }
+ canvas{
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ }
+
+ #log {
+ position: absolute;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ background-color: #EEE;
+ float: right;
+ width: 20%;
+ padding: 8px;
+ overflow-y: auto;
+ white-space: pre;
+ line-height: 1.5em;
+ }
+ </style>
+</head>
+
+<body>
+ <div id="log"></div>
+ <div style="width:75%;">
+ <canvas id="canvas"></canvas>
+ </div>
+ <script>
+ var logEntry = 1;
+ var logElement = document.getElementById('log');
+ var utils = Samples.utils;
+ var inputs = {
+ min: 20,
+ max: 80,
+ count: 8,
+ decimals: 2,
+ continuity: 1
+ };
+ var config;
+
+ function log(text) {
+ logElement.innerText += logEntry + '. ' + text + '\n';
+ logElement.scrollTop = logElement.scrollHeight;
+ logEntry++;
+ }
+
+ function generateData() {
+ return utils.numbers(inputs);
+ }
+ function generateLabels() {
+ return utils.months({count: inputs.count});
+ }
+
+ utils.srand(42);
+
+ config = {
+ type: 'line',
+ data: {
+ labels: generateLabels(),
+ datasets: [{
+ label: 'My First dataset',
+ backgroundColor: utils.color(0),
+ borderColor: utils.color(0),
+ data: generateData(),
+ fill: false,
+ }, {
+ label: 'My Second dataset',
+ fill: false,
+ backgroundColor: utils.color(1),
+ borderColor: utils.color(1),
+ data: generateData(),
+ }]
+ },
+ options: {
+ legend: {
+ onHover: function(event, legendItem) {
+ log('onHover: ' + legendItem.text);
+ },
+ onLeave: function(event, legendItem) {
+ log('onLeave: ' + legendItem.text);
+ },
+ onClick: function(event, legendItem) {
+ log('onClick:' + legendItem.text);
+ }
+ },
+ title: {
+ display: true,
+ text: 'Chart.js Line Chart'
+ },
+ scales: {
+ xAxes: [{
+ display: true,
+ scaleLabel: {
+ display: true,
+ labelString: 'Month'
+ }
+ }],
+ yAxes: [{
+ display: true,
+ scaleLabel: {
+ display: true,
+ labelString: 'Value'
+ }
+ }]
+ }
+ }
+ };
+
+ new Chart(document.getElementById('canvas'), config);
+ </script>
+</body>
+
+</html>
}, {
title: 'Point style',
path: 'legend/point-style.html'
+ }, {
+ title: 'Callbacks',
+ path: 'legend/callbacks.html'
}]
}, {
title: 'Tooltip',
},
onHover: null,
+ onLeave: null,
labels: {
boxWidth: 40,
// Contains hit boxes for each dataset (in dataset order)
this.legendHitBoxes = [];
+ /**
+ * @private
+ */
+ this._hoveredItem = null;
+
// Are we in doughnut mode which has a different data type
this.doughnutMode = false;
},
}
},
+ /**
+ * @private
+ */
+ _getLegendItemAt: function(x, y) {
+ var me = this;
+ var i, hitBox, lh;
+
+ if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) {
+ // See if we are touching one of the dataset boxes
+ lh = me.legendHitBoxes;
+ for (i = 0; i < lh.length; ++i) {
+ hitBox = lh[i];
+
+ if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) {
+ // Touching an element
+ return me.legendItems[i];
+ }
+ }
+ }
+
+ return null;
+ },
+
/**
* Handle an event
* @private
* @param {IEvent} event - The event to handle
- * @return {boolean} true if a change occured
*/
handleEvent: function(e) {
var me = this;
var opts = me.options;
var type = e.type === 'mouseup' ? 'click' : e.type;
- var changed = false;
+ var hoveredItem;
if (type === 'mousemove') {
- if (!opts.onHover) {
+ if (!opts.onHover && !opts.onLeave) {
return;
}
} else if (type === 'click') {
}
// Chart event already has relative position in it
- var x = e.x;
- var y = e.y;
+ hoveredItem = me._getLegendItemAt(e.x, e.y);
- if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) {
- // See if we are touching one of the dataset boxes
- var lh = me.legendHitBoxes;
- for (var i = 0; i < lh.length; ++i) {
- var hitBox = lh[i];
-
- if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) {
- // Touching an element
- if (type === 'click') {
- // use e.native for backwards compatibility
- opts.onClick.call(me, e.native, me.legendItems[i]);
- changed = true;
- break;
- } else if (type === 'mousemove') {
- // use e.native for backwards compatibility
- opts.onHover.call(me, e.native, me.legendItems[i]);
- changed = true;
- break;
- }
+ if (type === 'click') {
+ if (hoveredItem && opts.onClick) {
+ // use e.native for backwards compatibility
+ opts.onClick.call(me, e.native, hoveredItem);
+ }
+ } else {
+ if (opts.onLeave && hoveredItem !== me._hoveredItem) {
+ if (me._hoveredItem) {
+ opts.onLeave.call(me, e.native, me._hoveredItem);
}
+ me._hoveredItem = hoveredItem;
}
- }
- return changed;
+ if (opts.onHover && hoveredItem) {
+ // use e.native for backwards compatibility
+ opts.onHover.call(me, e.native, hoveredItem);
+ }
+ }
}
});
// a callback that will handle
onClick: jasmine.any(Function),
onHover: null,
+ onLeave: null,
labels: {
boxWidth: 40,
expect(chart.legend.options).toEqual(jasmine.objectContaining(Chart.defaults.global.legend));
});
});
+
+ describe('callbacks', function() {
+ it('should call onClick, onHover and onLeave at the correct times', function() {
+ var clickItem = null;
+ var hoverItem = null;
+ var leaveItem = null;
+
+ var chart = acquireChart({
+ type: 'line',
+ data: {
+ labels: ['A', 'B', 'C', 'D'],
+ datasets: [{
+ data: [10, 20, 30, 100]
+ }]
+ },
+ options: {
+ legend: {
+ onClick: function(_, item) {
+ clickItem = item;
+ },
+ onHover: function(_, item) {
+ hoverItem = item;
+ },
+ onLeave: function(_, item) {
+ leaveItem = item;
+ }
+ }
+ }
+ });
+
+ var hb = chart.legend.legendHitBoxes[0];
+ var el = {
+ x: hb.left + (hb.width / 2),
+ y: hb.top + (hb.height / 2)
+ };
+
+ jasmine.triggerMouseEvent(chart, 'click', el);
+
+ expect(clickItem).toBe(chart.legend.legendItems[0]);
+
+ jasmine.triggerMouseEvent(chart, 'mousemove', el);
+
+ expect(hoverItem).toBe(chart.legend.legendItems[0]);
+
+ jasmine.triggerMouseEvent(chart, 'mousemove', chart.getDatasetMeta(0).data[0]);
+
+ expect(leaveItem).toBe(chart.legend.legendItems[0]);
+ });
+ });
});