### Interactions
+* `interactions` are now limited to the chart area
* `{mode: 'label'}` was replaced with `{mode: 'index'}`
* `{mode: 'single'}` was replaced with `{mode: 'nearest', intersect: true}`
* `modes['X-axis']` was replaced with `{mode: 'index', intersect: false}`
* `Element._view`
* `TimeScale._getPixelForOffset`
* `TimeScale.getLabelWidth`
+* `Tooltip._lastActive`
### Renamed
scales: {
x: {
type: 'linear',
- position: 'bottom'
+ position: 'bottom',
+ beginAtZero: true
},
y: {
type: 'category',
import helpers from '../helpers/index';
import {isNumber} from '../helpers/helpers.math';
+import {_isPointInArea} from '../helpers/helpers.canvas';
/**
* Helper function to get relative position for an event
/**
* Helper function to check the items at the hovered index on the index scale
* @param {Chart} chart - the chart
+ * @param {string} axis - the axis mode. x|y|xy
+ * @param {object} position - the point to be nearest to
* @param {function} handler - the callback to execute for each visible item
* @return whether all scales were of a suitable type
*/
/**
* Helper function to get the items that intersect the event position
- * @param {ChartElement[]} items - elements to filter
+ * @param {Chart} chart - the chart
* @param {object} position - the point to be nearest to
+ * @param {string} axis - the axis mode. x|y|xy
* @return {ChartElement[]} the nearest items
*/
function getIntersectItems(chart, position, axis) {
const items = [];
+ if (!_isPointInArea(position, chart.chartArea)) {
+ return items;
+ }
+
const evaluationFunc = function(element, datasetIndex, index) {
if (element.inRange(position.x, position.y)) {
items.push({element, datasetIndex, index});
let minDistance = Number.POSITIVE_INFINITY;
let items = [];
+ if (!_isPointInArea(position, chart.chartArea)) {
+ return items;
+ }
+
const evaluationFunc = function(element, datasetIndex, index) {
if (intersect && !element.inRange(position.x, position.y)) {
return;
const {label, value} = chart.getDatasetMeta(datasetIndex).controller._getLabelAndValue(index);
return {
- label: label,
- value: value,
- index: index,
- datasetIndex: datasetIndex
+ label,
+ value,
+ index,
+ datasetIndex
};
}
const me = this;
me.opacity = 0;
me._active = [];
- me._lastActive = [];
me.initialize();
}
* @returns {boolean} true if the tooltip changed
*/
handleEvent(e) {
- var me = this;
- var options = me.options;
- var changed = false;
-
- me._lastActive = me._lastActive || [];
+ const me = this;
+ const options = me.options;
+ const lastActive = me._active || [];
+ let changed = false;
+ let active = [];
// Find Active Elements for tooltips
- if (e.type === 'mouseout') {
- me._active = [];
- } else {
- me._active = me._chart.getElementsAtEventForMode(e, options.mode, options);
+ if (e.type !== 'mouseout') {
+ active = me._chart.getElementsAtEventForMode(e, options.mode, options);
if (options.reverse) {
- me._active.reverse();
+ active.reverse();
}
}
// Remember Last Actives
- changed = !helpers._elementsEqual(me._active, me._lastActive);
+ changed = !helpers._elementsEqual(active, lastActive);
// Only handle target event on tooltip change
if (changed) {
- me._lastActive = me._active;
+ me._active = active;
if (options.enabled || options.custom) {
me._eventPosition = {
};
me.update(true);
- // me.pivot();
}
}
* @private
*/
export function _isPointInArea(point, area) {
- var epsilon = 1e-6; // 1e-6 is margin in pixels for accumulated error.
+ const epsilon = 0.5; // margin - to match rounded decimals
return point.x > area.left - epsilon && point.x < area.right + epsilon &&
point.y > area.top - epsilon && point.y < area.bottom + epsilon;
}]
},
options: {
+ scales: {
+ x: {
+ offset: true
+ }
+ },
elements: {
point: {
backgroundColor: 'rgb(100, 150, 200)',
var tooltip = chart.tooltip;
expect(chart.lastActive[0].element).toEqual(point);
- expect(tooltip._lastActive[0].element).toEqual(point);
+ expect(tooltip._active[0].element).toEqual(point);
// Update and confirm tooltip is updated
chart.update();
expect(chart.lastActive[0].element).toEqual(point);
- expect(tooltip._lastActive[0].element).toEqual(point);
+ expect(tooltip._active[0].element).toEqual(point);
});
it ('should update the metadata', function() {
type: 'click',
chart: chart,
native: true, // needed otherwise things its a DOM event
- x: 0,
- y: 0
+ x: chart.chartArea.left,
+ y: chart.chartArea.top
};
var elements = Chart.Interaction.modes.index(chart, evt, {intersect: false}).map(item => item.element);
type: 'click',
chart: chart,
native: true, // needed otherwise things its a DOM event
- x: 0,
- y: 0
+ x: chart.chartArea.left,
+ y: chart.chartArea.top
};
var elements = Chart.Interaction.modes.index(chart, evt, {axis: 'xy', intersect: false}).map(item => item.element);
type: 'click',
chart: chart,
native: true, // needed otherwise things its a DOM event
- x: 0,
- y: 0
+ x: chart.chartArea.left,
+ y: chart.chartArea.top
};
var elements = Chart.Interaction.modes.dataset(chart, evt, {axis: 'x', intersect: false});
type: 'click',
chart: chart,
native: true, // needed otherwise things its a DOM event
- x: 0,
- y: 0
+ x: chart.chartArea.left,
+ y: chart.chartArea.top
};
var elements = Chart.Interaction.modes.dataset(chart, evt, {axis: 'y', intersect: false});
type: 'click',
chart: chart,
native: true, // needed otherwise things its a DOM event
- x: 0,
- y: 0
+ x: chart.chartArea.left,
+ y: chart.chartArea.top
};
var elements = Chart.Interaction.modes.dataset(chart, evt, {intersect: false});
type: 'click',
chart: chart,
native: true, // needed otherwise things its a DOM event
- x: 0,
- y: 0
+ x: chart.chartArea.left,
+ y: chart.chartArea.top
};
// Nearest to 0,0 (top left) will be first point of dataset 2
bubbles: true,
cancelable: true,
clientX: rect.left + point.x,
- clientY: 0
+ clientY: rect.top + chart.chartArea.top + 5 // +5 to make tests work consistently
});
// Manually trigger rather than having an async test
expect(isPointInArea({x: -1e-12, y: -1e-12}, area)).toBe(true);
expect(isPointInArea({x: 512, y: 256}, area)).toBe(true);
expect(isPointInArea({x: 512 + 1e-12, y: 256 + 1e-12}, area)).toBe(true);
- expect(isPointInArea({x: -1e-3, y: 0}, area)).toBe(false);
- expect(isPointInArea({x: 0, y: 256 + 1e-3}, area)).toBe(false);
+ expect(isPointInArea({x: -0.5, y: 0}, area)).toBe(false);
+ expect(isPointInArea({x: 0, y: 256.5}, area)).toBe(false);
});
});
});