]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Add onLeave callback to legend (#6059)
authorJon Rimmer <jon.rimmer@gmail.com>
Sun, 24 Feb 2019 09:58:22 +0000 (09:58 +0000)
committerSimon Brunel <simonbrunel@users.noreply.github.com>
Sun, 24 Feb 2019 09:58:22 +0000 (10:58 +0100)
samples/legend/callbacks.html [new file with mode: 0644]
samples/samples.js
src/plugins/plugin.legend.js
test/specs/plugin.legend.tests.js

diff --git a/samples/legend/callbacks.html b/samples/legend/callbacks.html
new file mode 100644 (file)
index 0000000..aa71857
--- /dev/null
@@ -0,0 +1,124 @@
+<!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>
index 29ed1ff8190a27fb2def5f31b1c1674e40e56efd..bb0463f7e6e526a377247bb6e4ff3b4285efcf3c 100644 (file)
                }, {
                        title: 'Point style',
                        path: 'legend/point-style.html'
+               }, {
+                       title: 'Callbacks',
+                       path: 'legend/callbacks.html'
                }]
        }, {
                title: 'Tooltip',
index 2e832f98afd01e8a6c82593f227d5e758e6ae135..2c6870b279d722d0e4c2561b441bd082f40cd7ee 100644 (file)
@@ -30,6 +30,7 @@ defaults._set('global', {
                },
 
                onHover: null,
+               onLeave: null,
 
                labels: {
                        boxWidth: 40,
@@ -106,6 +107,11 @@ var Legend = Element.extend({
                // 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;
        },
@@ -458,20 +464,42 @@ var Legend = Element.extend({
                }
        },
 
+       /**
+        * @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') {
@@ -483,33 +511,26 @@ var Legend = Element.extend({
                }
 
                // 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);
+                       }
+               }
        }
 });
 
index fee715bdb1383341c4bc07d52dca4ce0b9f31fa7..e970c596a82a7c75052c7faa2e9e44153223c82d 100644 (file)
@@ -11,6 +11,7 @@ describe('Legend block tests', function() {
                        // a callback that will handle
                        onClick: jasmine.any(Function),
                        onHover: null,
+                       onLeave: null,
 
                        labels: {
                                boxWidth: 40,
@@ -653,4 +654,53 @@ describe('Legend block tests', function() {
                        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]);
+               });
+       });
 });