chartjs-plugin-boxselect
Version:
Chart.js plugin to select points on a chart with a click-and-drag box
250 lines (200 loc) • 7.4 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('chart.js')) :
typeof define === 'function' && define.amd ? define(['chart.js'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.boxselectplugin = factory(global.Chart));
}(this, (function (Chart) { 'use strict';
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var Chart__default = /*#__PURE__*/_interopDefaultLegacy(Chart);
var defaultOptions = {
select: {
enabled: true,
direction: 'xy',
selectboxBackgroundColor: 'rgba(66,133,244,0.2)',
selectboxBorderColor: '#48F',
},
callbacks: {
beforeSelect: function (startX, endX, startY, endY) {
return true;
},
afterSelect: function (startX, endX, startY, endY, datasets) {
}
}
};
function getOption(chart, category, name) {
return Chart__default['default'].helpers.getValueOrDefault(chart.options.plugins.boxselect[category] ? chart.options.plugins.boxselect[category][name] : undefined, defaultOptions[category][name]);
}
function getXScale(chart) {
return chart.data.datasets.length ? chart.scales[chart.getDatasetMeta(0).xAxisID] : null;
}
function getYScale(chart) {
return chart.scales[chart.getDatasetMeta(0).yAxisID];
}
function doSelect(chart, startX, endX, startY, endY) {
// swap start/end if user dragged from right to left
if (startX > endX) {
var tmp = startX;
startX = endX;
endX = tmp;
}
if (startY > endY) {
var tmp = startY;
startY = endY;
endY = tmp;
}
// notify delegate
var beforeSelectCallback = Chart__default['default'].helpers.getValueOrDefault(chart.options.plugins.boxselect.callbacks ? chart.options.plugins.boxselect.callbacks.beforeSelect : undefined, defaultOptions.callbacks.beforeSelect);
if (!beforeSelectCallback(startX, endX, startY, endY)) {
return false;
}
var datasets = [];
// filter dataset
for (var datasetIndex = 0; datasetIndex < chart.data.datasets.length; datasetIndex++) {
const sourceDataset = chart.data.datasets[datasetIndex];
var selectedDataset = {
data: [],
indexes: []
};
// if the dataset has labels, get them too
if (sourceDataset.labels) {
selectedDataset.labels = [];
}
// iterate data points
for (var dataIndex = 0; dataIndex < sourceDataset.data.length; dataIndex++) {
var dataPoint = sourceDataset.data[dataIndex];
let inX = true;
if (startX == null) ; else {
inX = (dataPoint.x >= startX && dataPoint.x <= endX);
}
let inY = true;
if (startY == null) ; else {
inY = (dataPoint.y >= startY && dataPoint.y <= endY);
}
if (inX && inY) {
selectedDataset.data.push({ ...dataPoint });
selectedDataset.indexes.push(dataIndex);
if (selectedDataset.labels) {
selectedDataset.labels.push(sourceDataset.labels[dataIndex]);
}
}
}
datasets.push(selectedDataset);
}
chart.boxselect.start = startX;
chart.boxselect.end = endX;
// chart.update();
var afterSelectCallback = getOption(chart, 'callbacks', 'afterSelect');
afterSelectCallback(startX, endX, startY, endY, datasets);
}
function drawSelectbox(chart) {
var borderColor = getOption(chart, 'select', 'selectboxBorderColor');
var fillColor = getOption(chart, 'select', 'selectboxBackgroundColor');
var direction = getOption(chart, 'select', 'direction');
chart.ctx.beginPath();
// if direction == xy, rectangle
// if direction == x, horizontal selection only
// if direction == y, vertical selection only
let xStart = chart.boxselect.dragStartX;
let yStart = chart.boxselect.dragStartY;
let xSize = chart.boxselect.x - chart.boxselect.dragStartX;
let ySize = chart.boxselect.y - chart.boxselect.dragStartY;
if (direction == 'x') {
var yScale = getYScale(chart);
yStart = yScale.getPixelForValue(yScale.max);
ySize = yScale.getPixelForValue(yScale.min) - yScale.getPixelForValue(yScale.max);
} else if (direction == 'y') {
var xScale = getXScale(chart);
xStart = xScale.getPixelForValue(xScale.max);
xSize = xScale.getPixelForValue(xScale.min) - xScale.getPixelForValue(xScale.max);
}
// x y width height
chart.ctx.rect(xStart, yStart, xSize, ySize);
chart.ctx.lineWidth = 1;
chart.ctx.strokeStyle = borderColor;
chart.ctx.fillStyle = fillColor;
chart.ctx.fill();
chart.ctx.fillStyle = '';
chart.ctx.stroke();
chart.ctx.closePath();
}
var boxselectPlugin = {
id: 'boxselect',
afterInit: function (chart) {
if (chart.config.options.scales.xAxes.length == 0) {
return
}
if (chart.options.plugins.boxselect === undefined) {
chart.options.plugins.boxselect = defaultOptions;
}
chart.boxselect = {
enabled: false,
x: null,
y: null,
dragStarted: false,
dragStartX: null,
dragEndX: null,
dragStartY: null,
dragEndY: null,
suppressTooltips: false,
};
},
afterEvent: function (chart, e) {
var chartType = chart.config.type;
if (chartType !== 'scatter' && chartType !== 'line') return;
// fix for Safari
var buttons = (e.native.buttons === undefined ? e.native.which : e.native.buttons);
if (e.native.type === 'mouseup') {
buttons = 0;
}
chart.boxselect.enabled = true;
// handle drag to select
var selectEnabled = getOption(chart, 'select', 'enabled');
if (buttons === 1 && !chart.boxselect.dragStarted && selectEnabled) {
chart.boxselect.dragStartX = e.x;
chart.boxselect.dragStartY = e.y;
chart.boxselect.dragStarted = true;
}
// handle drag to select
if (chart.boxselect.dragStarted && buttons === 0) {
chart.boxselect.dragStarted = false;
var direction = getOption(chart, 'select', 'direction');
// if direction == xy, rectangle
// if direction == x, horizontal selection only
// if direction == y, vertical selection only
var xScale = getXScale(chart);
var yScale = getYScale(chart);
var startX = xScale.getValueForPixel(chart.boxselect.dragStartX);
var endX = xScale.getValueForPixel(chart.boxselect.x);
var startY = yScale.getValueForPixel(chart.boxselect.dragStartY);
var endY = yScale.getValueForPixel(chart.boxselect.y);
if (direction == 'x') {
startY = null;
endY = null;
} else if (direction == 'y') {
startX = null;
endX = null;
}
if (Math.abs(chart.boxselect.dragStartX - chart.boxselect.x) > 1 && Math.abs(chart.boxselect.dragStartY - chart.boxselect.y) > 1) {
doSelect(chart, startX, endX, startY, endY);
}
}
chart.boxselect.x = e.x;
chart.boxselect.y = e.y;
chart.draw();
},
afterDraw: function (chart) {
if (!chart.boxselect.enabled) {
return;
}
if (chart.boxselect.dragStarted) {
drawSelectbox(chart);
}
return true;
},
beforeTooltipDraw: function (chart) {
// suppress tooltips on dragging
return !chart.boxselect.dragStarted && !chart.boxselect.suppressTooltips;
},
};
Chart__default['default'].plugins.register(boxselectPlugin);
return boxselectPlugin;
})));