1
0
Fork 0
mirror of https://github.com/DanielnetoDotCom/YouPHPTube synced 2025-10-03 09:49:28 +02:00

Show a report for total users chart

This commit is contained in:
Daniel Neto 2025-07-10 15:28:42 -03:00
parent 794938db72
commit 4d064adf1b
5397 changed files with 313100 additions and 365 deletions

9
node_modules/chartjs-plugin-zoom/LICENSE.md generated vendored Normal file
View file

@ -0,0 +1,9 @@
The MIT License (MIT)
Copyright (c) 2013-2021 chartjs-plugin-zoom contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

30
node_modules/chartjs-plugin-zoom/README.md generated vendored Normal file
View file

@ -0,0 +1,30 @@
# chartjs-plugin-zoom
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/chartjs/chartjs-plugin-zoom/ci.yml)](https://github.com/chartjs/chartjs-plugin-zoom/actions/workflows/ci.yml)
[![Coverage Status](https://coveralls.io/repos/github/chartjs/chartjs-plugin-zoom/badge.svg?branch=master)](https://coveralls.io/github/chartjs/chartjs-plugin-zoom?branch=master)
[![release](https://img.shields.io/github/v/release/chartjs/chartjs-plugin-zoom?include_prereleases)](https://github.com/chartjs/chartjs-plugin-zoom/releases)
[![npm (latest)](https://img.shields.io/npm/v/chartjs-plugin-zoom/latest)](https://www.npmjs.com/package/chartjs-plugin-zoom/v/latest)
[![npm (next)](https://img.shields.io/npm/v/chartjs-plugin-zoom/next)](https://www.npmjs.com/package/chartjs-plugin-zoom/v/next)
[![documentation](https://img.shields.io/static/v1?message=Documentation&color=informational)](https://www.chartjs.org/chartjs-plugin-zoom/index)
<a href="https://github.com/chartjs/awesome"><img src="https://awesome.re/badge-flat2.svg" alt="Awesome"></a>
A zoom and pan plugin for Chart.js >= 3.0.0
For Chart.js 2.6.0 to 2.9.x support, use [version 0.7.7 of this plugin](https://github.com/chartjs/chartjs-plugin-zoom/releases/tag/v0.7.7).
Panning can be done via the mouse or with a finger.
Zooming is done via the mouse wheel or via a pinch gesture. [Hammer.js](https://hammerjs.github.io/) is used for gesture recognition.
## Documentation
You can find documentation for chartjs-plugin-zoom at [www.chartjs.org/chartjs-plugin-zoom](https://www.chartjs.org/chartjs-plugin-zoom/).
Prior to v0.4.0, this plugin was known as 'Chart.Zoom.js'. Old versions are still available on npm under that name.
## Contributing
Before submitting an issue or a pull request to the project, please take a moment to look over the [contributing guidelines](CONTRIBUTING.md) first.
## License
chartjs-plugin-zoom.js is available under the [MIT license](https://opensource.org/licenses/MIT).

View file

@ -0,0 +1,972 @@
/*!
* chartjs-plugin-zoom v2.2.0
* https://www.chartjs.org/chartjs-plugin-zoom/2.2.0/
* (c) 2016-2024 chartjs-plugin-zoom Contributors
* Released under the MIT License
*/
import Hammer from 'hammerjs';
import { each, valueOrDefault, almostEquals, callback, sign, getRelativePosition, _isPointInArea } from 'chart.js/helpers';
const getModifierKey = opts => opts && opts.enabled && opts.modifierKey;
const keyPressed = (key, event) => key && event[key + 'Key'];
const keyNotPressed = (key, event) => key && !event[key + 'Key'];
function directionEnabled(mode, dir, chart) {
if (mode === undefined) {
return true;
} else if (typeof mode === 'string') {
return mode.indexOf(dir) !== -1;
} else if (typeof mode === 'function') {
return mode({chart}).indexOf(dir) !== -1;
}
return false;
}
function directionsEnabled(mode, chart) {
if (typeof mode === 'function') {
mode = mode({chart});
}
if (typeof mode === 'string') {
return {x: mode.indexOf('x') !== -1, y: mode.indexOf('y') !== -1};
}
return {x: false, y: false};
}
function debounce(fn, delay) {
let timeout;
return function() {
clearTimeout(timeout);
timeout = setTimeout(fn, delay);
return delay;
};
}
function getScaleUnderPoint({x, y}, chart) {
const scales = chart.scales;
const scaleIds = Object.keys(scales);
for (let i = 0; i < scaleIds.length; i++) {
const scale = scales[scaleIds[i]];
if (y >= scale.top && y <= scale.bottom && x >= scale.left && x <= scale.right) {
return scale;
}
}
return null;
}
function getEnabledScalesByPoint(options, point, chart) {
const {mode = 'xy', scaleMode, overScaleMode} = options || {};
const scale = getScaleUnderPoint(point, chart);
const enabled = directionsEnabled(mode, chart);
const scaleEnabled = directionsEnabled(scaleMode, chart);
if (overScaleMode) {
const overScaleEnabled = directionsEnabled(overScaleMode, chart);
for (const axis of ['x', 'y']) {
if (overScaleEnabled[axis]) {
scaleEnabled[axis] = enabled[axis];
enabled[axis] = false;
}
}
}
if (scale && scaleEnabled[scale.axis]) {
return [scale];
}
const enabledScales = [];
each(chart.scales, function(scaleItem) {
if (enabled[scaleItem.axis]) {
enabledScales.push(scaleItem);
}
});
return enabledScales;
}
const chartStates = new WeakMap();
function getState(chart) {
let state = chartStates.get(chart);
if (!state) {
state = {
originalScaleLimits: {},
updatedScaleLimits: {},
handlers: {},
panDelta: {},
dragging: false,
panning: false
};
chartStates.set(chart, state);
}
return state;
}
function removeState(chart) {
chartStates.delete(chart);
}
function zoomDelta(val, min, range, newRange) {
const minPercent = Math.max(0, Math.min(1, (val - min) / range || 0));
const maxPercent = 1 - minPercent;
return {
min: newRange * minPercent,
max: newRange * maxPercent
};
}
function getValueAtPoint(scale, point) {
const pixel = scale.isHorizontal() ? point.x : point.y;
return scale.getValueForPixel(pixel);
}
function linearZoomDelta(scale, zoom, center) {
const range = scale.max - scale.min;
const newRange = range * (zoom - 1);
const centerValue = getValueAtPoint(scale, center);
return zoomDelta(centerValue, scale.min, range, newRange);
}
function logarithmicZoomRange(scale, zoom, center) {
const centerValue = getValueAtPoint(scale, center);
if (centerValue === undefined) {
return {min: scale.min, max: scale.max};
}
const logMin = Math.log10(scale.min);
const logMax = Math.log10(scale.max);
const logCenter = Math.log10(centerValue);
const logRange = logMax - logMin;
const newLogRange = logRange * (zoom - 1);
const delta = zoomDelta(logCenter, logMin, logRange, newLogRange);
return {
min: Math.pow(10, logMin + delta.min),
max: Math.pow(10, logMax - delta.max),
};
}
function getScaleLimits(scale, limits) {
return limits && (limits[scale.id] || limits[scale.axis]) || {};
}
function getLimit(state, scale, scaleLimits, prop, fallback) {
let limit = scaleLimits[prop];
if (limit === 'original') {
const original = state.originalScaleLimits[scale.id][prop];
limit = valueOrDefault(original.options, original.scale);
}
return valueOrDefault(limit, fallback);
}
function linearRange(scale, pixel0, pixel1) {
const v0 = scale.getValueForPixel(pixel0);
const v1 = scale.getValueForPixel(pixel1);
return {
min: Math.min(v0, v1),
max: Math.max(v0, v1)
};
}
function fixRange(range, {min, max, minLimit, maxLimit}, originalLimits) {
const offset = (range - max + min) / 2;
min -= offset;
max += offset;
const origMin = originalLimits.min.options ?? originalLimits.min.scale;
const origMax = originalLimits.max.options ?? originalLimits.max.scale;
const epsilon = range / 1e6;
if (almostEquals(min, origMin, epsilon)) {
min = origMin;
}
if (almostEquals(max, origMax, epsilon)) {
max = origMax;
}
if (min < minLimit) {
min = minLimit;
max = Math.min(minLimit + range, maxLimit);
} else if (max > maxLimit) {
max = maxLimit;
min = Math.max(maxLimit - range, minLimit);
}
return {min, max};
}
function updateRange(scale, {min, max}, limits, zoom = false) {
const state = getState(scale.chart);
const {options: scaleOpts} = scale;
const scaleLimits = getScaleLimits(scale, limits);
const {minRange = 0} = scaleLimits;
const minLimit = getLimit(state, scale, scaleLimits, 'min', -Infinity);
const maxLimit = getLimit(state, scale, scaleLimits, 'max', Infinity);
if (zoom === 'pan' && (min < minLimit || max > maxLimit)) {
return true;
}
const scaleRange = scale.max - scale.min;
const range = zoom ? Math.max(max - min, minRange) : scaleRange;
if (zoom && range === minRange && scaleRange <= minRange) {
return true;
}
const newRange = fixRange(range, {min, max, minLimit, maxLimit}, state.originalScaleLimits[scale.id]);
scaleOpts.min = newRange.min;
scaleOpts.max = newRange.max;
state.updatedScaleLimits[scale.id] = newRange;
return scale.parse(newRange.min) !== scale.min || scale.parse(newRange.max) !== scale.max;
}
function zoomNumericalScale(scale, zoom, center, limits) {
const delta = linearZoomDelta(scale, zoom, center);
const newRange = {min: scale.min + delta.min, max: scale.max - delta.max};
return updateRange(scale, newRange, limits, true);
}
function zoomLogarithmicScale(scale, zoom, center, limits) {
const newRange = logarithmicZoomRange(scale, zoom, center);
return updateRange(scale, newRange, limits, true);
}
function zoomRectNumericalScale(scale, from, to, limits) {
updateRange(scale, linearRange(scale, from, to), limits, true);
}
const integerChange = (v) => v === 0 || isNaN(v) ? 0 : v < 0 ? Math.min(Math.round(v), -1) : Math.max(Math.round(v), 1);
function existCategoryFromMaxZoom(scale) {
const labels = scale.getLabels();
const maxIndex = labels.length - 1;
if (scale.min > 0) {
scale.min -= 1;
}
if (scale.max < maxIndex) {
scale.max += 1;
}
}
function zoomCategoryScale(scale, zoom, center, limits) {
const delta = linearZoomDelta(scale, zoom, center);
if (scale.min === scale.max && zoom < 1) {
existCategoryFromMaxZoom(scale);
}
const newRange = {min: scale.min + integerChange(delta.min), max: scale.max - integerChange(delta.max)};
return updateRange(scale, newRange, limits, true);
}
function scaleLength(scale) {
return scale.isHorizontal() ? scale.width : scale.height;
}
function panCategoryScale(scale, delta, limits) {
const labels = scale.getLabels();
const lastLabelIndex = labels.length - 1;
let {min, max} = scale;
const range = Math.max(max - min, 1);
const stepDelta = Math.round(scaleLength(scale) / Math.max(range, 10));
const stepSize = Math.round(Math.abs(delta / stepDelta));
let applied;
if (delta < -stepDelta) {
max = Math.min(max + stepSize, lastLabelIndex);
min = range === 1 ? max : max - range;
applied = max === lastLabelIndex;
} else if (delta > stepDelta) {
min = Math.max(0, min - stepSize);
max = range === 1 ? min : min + range;
applied = min === 0;
}
return updateRange(scale, {min, max}, limits) || applied;
}
const OFFSETS = {
second: 500,
minute: 30 * 1000,
hour: 30 * 60 * 1000,
day: 12 * 60 * 60 * 1000,
week: 3.5 * 24 * 60 * 60 * 1000,
month: 15 * 24 * 60 * 60 * 1000,
quarter: 60 * 24 * 60 * 60 * 1000,
year: 182 * 24 * 60 * 60 * 1000
};
function panNumericalScale(scale, delta, limits, pan = false) {
const {min: prevStart, max: prevEnd, options} = scale;
const round = options.time && options.time.round;
const offset = OFFSETS[round] || 0;
const newMin = scale.getValueForPixel(scale.getPixelForValue(prevStart + offset) - delta);
const newMax = scale.getValueForPixel(scale.getPixelForValue(prevEnd + offset) - delta);
if (isNaN(newMin) || isNaN(newMax)) {
return true;
}
return updateRange(scale, {min: newMin, max: newMax}, limits, pan ? 'pan' : false);
}
function panNonLinearScale(scale, delta, limits) {
return panNumericalScale(scale, delta, limits, true);
}
const zoomFunctions = {
category: zoomCategoryScale,
default: zoomNumericalScale,
logarithmic: zoomLogarithmicScale,
};
const zoomRectFunctions = {
default: zoomRectNumericalScale,
};
const panFunctions = {
category: panCategoryScale,
default: panNumericalScale,
logarithmic: panNonLinearScale,
timeseries: panNonLinearScale,
};
function shouldUpdateScaleLimits(scale, originalScaleLimits, updatedScaleLimits) {
const {id, options: {min, max}} = scale;
if (!originalScaleLimits[id] || !updatedScaleLimits[id]) {
return true;
}
const previous = updatedScaleLimits[id];
return previous.min !== min || previous.max !== max;
}
function removeMissingScales(limits, scales) {
each(limits, (opt, key) => {
if (!scales[key]) {
delete limits[key];
}
});
}
function storeOriginalScaleLimits(chart, state) {
const {scales} = chart;
const {originalScaleLimits, updatedScaleLimits} = state;
each(scales, function(scale) {
if (shouldUpdateScaleLimits(scale, originalScaleLimits, updatedScaleLimits)) {
originalScaleLimits[scale.id] = {
min: {scale: scale.min, options: scale.options.min},
max: {scale: scale.max, options: scale.options.max},
};
}
});
removeMissingScales(originalScaleLimits, scales);
removeMissingScales(updatedScaleLimits, scales);
return originalScaleLimits;
}
function doZoom(scale, amount, center, limits) {
const fn = zoomFunctions[scale.type] || zoomFunctions.default;
callback(fn, [scale, amount, center, limits]);
}
function doZoomRect(scale, from, to, limits) {
const fn = zoomRectFunctions[scale.type] || zoomRectFunctions.default;
callback(fn, [scale, from, to, limits]);
}
function getCenter(chart) {
const ca = chart.chartArea;
return {
x: (ca.left + ca.right) / 2,
y: (ca.top + ca.bottom) / 2,
};
}
function zoom(chart, amount, transition = 'none', trigger = 'api') {
const {x = 1, y = 1, focalPoint = getCenter(chart)} = typeof amount === 'number' ? {x: amount, y: amount} : amount;
const state = getState(chart);
const {options: {limits, zoom: zoomOptions}} = state;
storeOriginalScaleLimits(chart, state);
const xEnabled = x !== 1;
const yEnabled = y !== 1;
const enabledScales = getEnabledScalesByPoint(zoomOptions, focalPoint, chart);
each(enabledScales || chart.scales, function(scale) {
if (scale.isHorizontal() && xEnabled) {
doZoom(scale, x, focalPoint, limits);
} else if (!scale.isHorizontal() && yEnabled) {
doZoom(scale, y, focalPoint, limits);
}
});
chart.update(transition);
callback(zoomOptions.onZoom, [{chart, trigger}]);
}
function zoomRect(chart, p0, p1, transition = 'none', trigger = 'api') {
const state = getState(chart);
const {options: {limits, zoom: zoomOptions}} = state;
const {mode = 'xy'} = zoomOptions;
storeOriginalScaleLimits(chart, state);
const xEnabled = directionEnabled(mode, 'x', chart);
const yEnabled = directionEnabled(mode, 'y', chart);
each(chart.scales, function(scale) {
if (scale.isHorizontal() && xEnabled) {
doZoomRect(scale, p0.x, p1.x, limits);
} else if (!scale.isHorizontal() && yEnabled) {
doZoomRect(scale, p0.y, p1.y, limits);
}
});
chart.update(transition);
callback(zoomOptions.onZoom, [{chart, trigger}]);
}
function zoomScale(chart, scaleId, range, transition = 'none', trigger = 'api') {
const state = getState(chart);
storeOriginalScaleLimits(chart, state);
const scale = chart.scales[scaleId];
updateRange(scale, range, undefined, true);
chart.update(transition);
callback(state.options.zoom?.onZoom, [{chart, trigger}]);
}
function resetZoom(chart, transition = 'default') {
const state = getState(chart);
const originalScaleLimits = storeOriginalScaleLimits(chart, state);
each(chart.scales, function(scale) {
const scaleOptions = scale.options;
if (originalScaleLimits[scale.id]) {
scaleOptions.min = originalScaleLimits[scale.id].min.options;
scaleOptions.max = originalScaleLimits[scale.id].max.options;
} else {
delete scaleOptions.min;
delete scaleOptions.max;
}
delete state.updatedScaleLimits[scale.id];
});
chart.update(transition);
callback(state.options.zoom.onZoomComplete, [{chart}]);
}
function getOriginalRange(state, scaleId) {
const original = state.originalScaleLimits[scaleId];
if (!original) {
return;
}
const {min, max} = original;
return valueOrDefault(max.options, max.scale) - valueOrDefault(min.options, min.scale);
}
function getZoomLevel(chart) {
const state = getState(chart);
let min = 1;
let max = 1;
each(chart.scales, function(scale) {
const origRange = getOriginalRange(state, scale.id);
if (origRange) {
const level = Math.round(origRange / (scale.max - scale.min) * 100) / 100;
min = Math.min(min, level);
max = Math.max(max, level);
}
});
return min < 1 ? min : max;
}
function panScale(scale, delta, limits, state) {
const {panDelta} = state;
const storedDelta = panDelta[scale.id] || 0;
if (sign(storedDelta) === sign(delta)) {
delta += storedDelta;
}
const fn = panFunctions[scale.type] || panFunctions.default;
if (callback(fn, [scale, delta, limits])) {
panDelta[scale.id] = 0;
} else {
panDelta[scale.id] = delta;
}
}
function pan(chart, delta, enabledScales, transition = 'none') {
const {x = 0, y = 0} = typeof delta === 'number' ? {x: delta, y: delta} : delta;
const state = getState(chart);
const {options: {pan: panOptions, limits}} = state;
const {onPan} = panOptions || {};
storeOriginalScaleLimits(chart, state);
const xEnabled = x !== 0;
const yEnabled = y !== 0;
each(enabledScales || chart.scales, function(scale) {
if (scale.isHorizontal() && xEnabled) {
panScale(scale, x, limits, state);
} else if (!scale.isHorizontal() && yEnabled) {
panScale(scale, y, limits, state);
}
});
chart.update(transition);
callback(onPan, [{chart}]);
}
function getInitialScaleBounds(chart) {
const state = getState(chart);
storeOriginalScaleLimits(chart, state);
const scaleBounds = {};
for (const scaleId of Object.keys(chart.scales)) {
const {min, max} = state.originalScaleLimits[scaleId] || {min: {}, max: {}};
scaleBounds[scaleId] = {min: min.scale, max: max.scale};
}
return scaleBounds;
}
function getZoomedScaleBounds(chart) {
const state = getState(chart);
const scaleBounds = {};
for (const scaleId of Object.keys(chart.scales)) {
scaleBounds[scaleId] = state.updatedScaleLimits[scaleId];
}
return scaleBounds;
}
function isZoomedOrPanned(chart) {
const scaleBounds = getInitialScaleBounds(chart);
for (const scaleId of Object.keys(chart.scales)) {
const {min: originalMin, max: originalMax} = scaleBounds[scaleId];
if (originalMin !== undefined && chart.scales[scaleId].min !== originalMin) {
return true;
}
if (originalMax !== undefined && chart.scales[scaleId].max !== originalMax) {
return true;
}
}
return false;
}
function isZoomingOrPanning(chart) {
const state = getState(chart);
return state.panning || state.dragging;
}
const clamp = (x, from, to) => Math.min(to, Math.max(from, x));
function removeHandler(chart, type) {
const {handlers} = getState(chart);
const handler = handlers[type];
if (handler && handler.target) {
handler.target.removeEventListener(type, handler);
delete handlers[type];
}
}
function addHandler(chart, target, type, handler) {
const {handlers, options} = getState(chart);
const oldHandler = handlers[type];
if (oldHandler && oldHandler.target === target) {
return;
}
removeHandler(chart, type);
handlers[type] = (event) => handler(chart, event, options);
handlers[type].target = target;
const passive = type === 'wheel' ? false : undefined;
target.addEventListener(type, handlers[type], {passive});
}
function mouseMove(chart, event) {
const state = getState(chart);
if (state.dragStart) {
state.dragging = true;
state.dragEnd = event;
chart.update('none');
}
}
function keyDown(chart, event) {
const state = getState(chart);
if (!state.dragStart || event.key !== 'Escape') {
return;
}
removeHandler(chart, 'keydown');
state.dragging = false;
state.dragStart = state.dragEnd = null;
chart.update('none');
}
function getPointPosition(event, chart) {
if (event.target !== chart.canvas) {
const canvasArea = chart.canvas.getBoundingClientRect();
return {
x: event.clientX - canvasArea.left,
y: event.clientY - canvasArea.top,
};
}
return getRelativePosition(event, chart);
}
function zoomStart(chart, event, zoomOptions) {
const {onZoomStart, onZoomRejected} = zoomOptions;
if (onZoomStart) {
const point = getPointPosition(event, chart);
if (callback(onZoomStart, [{chart, event, point}]) === false) {
callback(onZoomRejected, [{chart, event}]);
return false;
}
}
}
function mouseDown(chart, event) {
if (chart.legend) {
const point = getRelativePosition(event, chart);
if (_isPointInArea(point, chart.legend)) {
return;
}
}
const state = getState(chart);
const {pan: panOptions, zoom: zoomOptions = {}} = state.options;
if (
event.button !== 0 ||
keyPressed(getModifierKey(panOptions), event) ||
keyNotPressed(getModifierKey(zoomOptions.drag), event)
) {
return callback(zoomOptions.onZoomRejected, [{chart, event}]);
}
if (zoomStart(chart, event, zoomOptions) === false) {
return;
}
state.dragStart = event;
addHandler(chart, chart.canvas.ownerDocument, 'mousemove', mouseMove);
addHandler(chart, window.document, 'keydown', keyDown);
}
function applyAspectRatio({begin, end}, aspectRatio) {
let width = end.x - begin.x;
let height = end.y - begin.y;
const ratio = Math.abs(width / height);
if (ratio > aspectRatio) {
width = Math.sign(width) * Math.abs(height * aspectRatio);
} else if (ratio < aspectRatio) {
height = Math.sign(height) * Math.abs(width / aspectRatio);
}
end.x = begin.x + width;
end.y = begin.y + height;
}
function applyMinMaxProps(rect, chartArea, points, {min, max, prop}) {
rect[min] = clamp(Math.min(points.begin[prop], points.end[prop]), chartArea[min], chartArea[max]);
rect[max] = clamp(Math.max(points.begin[prop], points.end[prop]), chartArea[min], chartArea[max]);
}
function getRelativePoints(chart, pointEvents, maintainAspectRatio) {
const points = {
begin: getPointPosition(pointEvents.dragStart, chart),
end: getPointPosition(pointEvents.dragEnd, chart),
};
if (maintainAspectRatio) {
const aspectRatio = chart.chartArea.width / chart.chartArea.height;
applyAspectRatio(points, aspectRatio);
}
return points;
}
function computeDragRect(chart, mode, pointEvents, maintainAspectRatio) {
const xEnabled = directionEnabled(mode, 'x', chart);
const yEnabled = directionEnabled(mode, 'y', chart);
const {top, left, right, bottom, width: chartWidth, height: chartHeight} = chart.chartArea;
const rect = {top, left, right, bottom};
const points = getRelativePoints(chart, pointEvents, maintainAspectRatio && xEnabled && yEnabled);
if (xEnabled) {
applyMinMaxProps(rect, chart.chartArea, points, {min: 'left', max: 'right', prop: 'x'});
}
if (yEnabled) {
applyMinMaxProps(rect, chart.chartArea, points, {min: 'top', max: 'bottom', prop: 'y'});
}
const width = rect.right - rect.left;
const height = rect.bottom - rect.top;
return {
...rect,
width,
height,
zoomX: xEnabled && width ? 1 + ((chartWidth - width) / chartWidth) : 1,
zoomY: yEnabled && height ? 1 + ((chartHeight - height) / chartHeight) : 1
};
}
function mouseUp(chart, event) {
const state = getState(chart);
if (!state.dragStart) {
return;
}
removeHandler(chart, 'mousemove');
const {mode, onZoomComplete, drag: {threshold = 0, maintainAspectRatio}} = state.options.zoom;
const rect = computeDragRect(chart, mode, {dragStart: state.dragStart, dragEnd: event}, maintainAspectRatio);
const distanceX = directionEnabled(mode, 'x', chart) ? rect.width : 0;
const distanceY = directionEnabled(mode, 'y', chart) ? rect.height : 0;
const distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
state.dragStart = state.dragEnd = null;
if (distance <= threshold) {
state.dragging = false;
chart.update('none');
return;
}
zoomRect(chart, {x: rect.left, y: rect.top}, {x: rect.right, y: rect.bottom}, 'zoom', 'drag');
state.dragging = false;
state.filterNextClick = true;
callback(onZoomComplete, [{chart}]);
}
function wheelPreconditions(chart, event, zoomOptions) {
if (keyNotPressed(getModifierKey(zoomOptions.wheel), event)) {
callback(zoomOptions.onZoomRejected, [{chart, event}]);
return;
}
if (zoomStart(chart, event, zoomOptions) === false) {
return;
}
if (event.cancelable) {
event.preventDefault();
}
if (event.deltaY === undefined) {
return;
}
return true;
}
function wheel(chart, event) {
const {handlers: {onZoomComplete}, options: {zoom: zoomOptions}} = getState(chart);
if (!wheelPreconditions(chart, event, zoomOptions)) {
return;
}
const rect = event.target.getBoundingClientRect();
const speed = zoomOptions.wheel.speed;
const percentage = event.deltaY >= 0 ? 2 - 1 / (1 - speed) : 1 + speed;
const amount = {
x: percentage,
y: percentage,
focalPoint: {
x: event.clientX - rect.left,
y: event.clientY - rect.top
}
};
zoom(chart, amount, 'zoom', 'wheel');
callback(onZoomComplete, [{chart}]);
}
function addDebouncedHandler(chart, name, handler, delay) {
if (handler) {
getState(chart).handlers[name] = debounce(() => callback(handler, [{chart}]), delay);
}
}
function addListeners(chart, options) {
const canvas = chart.canvas;
const {wheel: wheelOptions, drag: dragOptions, onZoomComplete} = options.zoom;
if (wheelOptions.enabled) {
addHandler(chart, canvas, 'wheel', wheel);
addDebouncedHandler(chart, 'onZoomComplete', onZoomComplete, 250);
} else {
removeHandler(chart, 'wheel');
}
if (dragOptions.enabled) {
addHandler(chart, canvas, 'mousedown', mouseDown);
addHandler(chart, canvas.ownerDocument, 'mouseup', mouseUp);
} else {
removeHandler(chart, 'mousedown');
removeHandler(chart, 'mousemove');
removeHandler(chart, 'mouseup');
removeHandler(chart, 'keydown');
}
}
function removeListeners(chart) {
removeHandler(chart, 'mousedown');
removeHandler(chart, 'mousemove');
removeHandler(chart, 'mouseup');
removeHandler(chart, 'wheel');
removeHandler(chart, 'click');
removeHandler(chart, 'keydown');
}
function createEnabler(chart, state) {
return function(recognizer, event) {
const {pan: panOptions, zoom: zoomOptions = {}} = state.options;
if (!panOptions || !panOptions.enabled) {
return false;
}
const srcEvent = event && event.srcEvent;
if (!srcEvent) {
return true;
}
if (!state.panning && event.pointerType === 'mouse' && (
keyNotPressed(getModifierKey(panOptions), srcEvent) || keyPressed(getModifierKey(zoomOptions.drag), srcEvent))
) {
callback(panOptions.onPanRejected, [{chart, event}]);
return false;
}
return true;
};
}
function pinchAxes(p0, p1) {
const pinchX = Math.abs(p0.clientX - p1.clientX);
const pinchY = Math.abs(p0.clientY - p1.clientY);
const p = pinchX / pinchY;
let x, y;
if (p > 0.3 && p < 1.7) {
x = y = true;
} else if (pinchX > pinchY) {
x = true;
} else {
y = true;
}
return {x, y};
}
function handlePinch(chart, state, e) {
if (state.scale) {
const {center, pointers} = e;
const zoomPercent = 1 / state.scale * e.scale;
const rect = e.target.getBoundingClientRect();
const pinch = pinchAxes(pointers[0], pointers[1]);
const mode = state.options.zoom.mode;
const amount = {
x: pinch.x && directionEnabled(mode, 'x', chart) ? zoomPercent : 1,
y: pinch.y && directionEnabled(mode, 'y', chart) ? zoomPercent : 1,
focalPoint: {
x: center.x - rect.left,
y: center.y - rect.top
}
};
zoom(chart, amount, 'zoom', 'pinch');
state.scale = e.scale;
}
}
function startPinch(chart, state, event) {
if (state.options.zoom.pinch.enabled) {
const point = getRelativePosition(event, chart);
if (callback(state.options.zoom.onZoomStart, [{chart, event, point}]) === false) {
state.scale = null;
callback(state.options.zoom.onZoomRejected, [{chart, event}]);
} else {
state.scale = 1;
}
}
}
function endPinch(chart, state, e) {
if (state.scale) {
handlePinch(chart, state, e);
state.scale = null;
callback(state.options.zoom.onZoomComplete, [{chart}]);
}
}
function handlePan(chart, state, e) {
const delta = state.delta;
if (delta) {
state.panning = true;
pan(chart, {x: e.deltaX - delta.x, y: e.deltaY - delta.y}, state.panScales);
state.delta = {x: e.deltaX, y: e.deltaY};
}
}
function startPan(chart, state, event) {
const {enabled, onPanStart, onPanRejected} = state.options.pan;
if (!enabled) {
return;
}
const rect = event.target.getBoundingClientRect();
const point = {
x: event.center.x - rect.left,
y: event.center.y - rect.top
};
if (callback(onPanStart, [{chart, event, point}]) === false) {
return callback(onPanRejected, [{chart, event}]);
}
state.panScales = getEnabledScalesByPoint(state.options.pan, point, chart);
state.delta = {x: 0, y: 0};
handlePan(chart, state, event);
}
function endPan(chart, state) {
state.delta = null;
if (state.panning) {
state.panning = false;
state.filterNextClick = true;
callback(state.options.pan.onPanComplete, [{chart}]);
}
}
const hammers = new WeakMap();
function startHammer(chart, options) {
const state = getState(chart);
const canvas = chart.canvas;
const {pan: panOptions, zoom: zoomOptions} = options;
const mc = new Hammer.Manager(canvas);
if (zoomOptions && zoomOptions.pinch.enabled) {
mc.add(new Hammer.Pinch());
mc.on('pinchstart', (e) => startPinch(chart, state, e));
mc.on('pinch', (e) => handlePinch(chart, state, e));
mc.on('pinchend', (e) => endPinch(chart, state, e));
}
if (panOptions && panOptions.enabled) {
mc.add(new Hammer.Pan({
threshold: panOptions.threshold,
enable: createEnabler(chart, state)
}));
mc.on('panstart', (e) => startPan(chart, state, e));
mc.on('panmove', (e) => handlePan(chart, state, e));
mc.on('panend', () => endPan(chart, state));
}
hammers.set(chart, mc);
}
function stopHammer(chart) {
const mc = hammers.get(chart);
if (mc) {
mc.remove('pinchstart');
mc.remove('pinch');
mc.remove('pinchend');
mc.remove('panstart');
mc.remove('pan');
mc.remove('panend');
mc.destroy();
hammers.delete(chart);
}
}
function hammerOptionsChanged(oldOptions, newOptions) {
const {pan: oldPan, zoom: oldZoom} = oldOptions;
const {pan: newPan, zoom: newZoom} = newOptions;
if (oldZoom?.zoom?.pinch?.enabled !== newZoom?.zoom?.pinch?.enabled) {
return true;
}
if (oldPan?.enabled !== newPan?.enabled) {
return true;
}
if (oldPan?.threshold !== newPan?.threshold) {
return true;
}
return false;
}
var version = "2.2.0";
function draw(chart, caller, options) {
const dragOptions = options.zoom.drag;
const {dragStart, dragEnd} = getState(chart);
if (dragOptions.drawTime !== caller || !dragEnd) {
return;
}
const {left, top, width, height} = computeDragRect(chart, options.zoom.mode, {dragStart, dragEnd}, dragOptions.maintainAspectRatio);
const ctx = chart.ctx;
ctx.save();
ctx.beginPath();
ctx.fillStyle = dragOptions.backgroundColor || 'rgba(225,225,225,0.3)';
ctx.fillRect(left, top, width, height);
if (dragOptions.borderWidth > 0) {
ctx.lineWidth = dragOptions.borderWidth;
ctx.strokeStyle = dragOptions.borderColor || 'rgba(225,225,225)';
ctx.strokeRect(left, top, width, height);
}
ctx.restore();
}
var plugin = {
id: 'zoom',
version,
defaults: {
pan: {
enabled: false,
mode: 'xy',
threshold: 10,
modifierKey: null,
},
zoom: {
wheel: {
enabled: false,
speed: 0.1,
modifierKey: null
},
drag: {
enabled: false,
drawTime: 'beforeDatasetsDraw',
modifierKey: null
},
pinch: {
enabled: false
},
mode: 'xy',
}
},
start: function(chart, _args, options) {
const state = getState(chart);
state.options = options;
if (Object.prototype.hasOwnProperty.call(options.zoom, 'enabled')) {
console.warn('The option `zoom.enabled` is no longer supported. Please use `zoom.wheel.enabled`, `zoom.drag.enabled`, or `zoom.pinch.enabled`.');
}
if (Object.prototype.hasOwnProperty.call(options.zoom, 'overScaleMode')
|| Object.prototype.hasOwnProperty.call(options.pan, 'overScaleMode')) {
console.warn('The option `overScaleMode` is deprecated. Please use `scaleMode` instead (and update `mode` as desired).');
}
if (Hammer) {
startHammer(chart, options);
}
chart.pan = (delta, panScales, transition) => pan(chart, delta, panScales, transition);
chart.zoom = (args, transition) => zoom(chart, args, transition);
chart.zoomRect = (p0, p1, transition) => zoomRect(chart, p0, p1, transition);
chart.zoomScale = (id, range, transition) => zoomScale(chart, id, range, transition);
chart.resetZoom = (transition) => resetZoom(chart, transition);
chart.getZoomLevel = () => getZoomLevel(chart);
chart.getInitialScaleBounds = () => getInitialScaleBounds(chart);
chart.getZoomedScaleBounds = () => getZoomedScaleBounds(chart);
chart.isZoomedOrPanned = () => isZoomedOrPanned(chart);
chart.isZoomingOrPanning = () => isZoomingOrPanning(chart);
},
beforeEvent(chart, {event}) {
if (isZoomingOrPanning(chart)) {
return false;
}
if (event.type === 'click' || event.type === 'mouseup') {
const state = getState(chart);
if (state.filterNextClick) {
state.filterNextClick = false;
return false;
}
}
},
beforeUpdate: function(chart, args, options) {
const state = getState(chart);
const previousOptions = state.options;
state.options = options;
if (hammerOptionsChanged(previousOptions, options)) {
stopHammer(chart);
startHammer(chart, options);
}
addListeners(chart, options);
},
beforeDatasetsDraw(chart, _args, options) {
draw(chart, 'beforeDatasetsDraw', options);
},
afterDatasetsDraw(chart, _args, options) {
draw(chart, 'afterDatasetsDraw', options);
},
beforeDraw(chart, _args, options) {
draw(chart, 'beforeDraw', options);
},
afterDraw(chart, _args, options) {
draw(chart, 'afterDraw', options);
},
stop: function(chart) {
removeListeners(chart);
if (Hammer) {
stopHammer(chart);
}
removeState(chart);
},
panFunctions,
zoomFunctions,
zoomRectFunctions,
};
export { plugin as default, pan, resetZoom, zoom, zoomRect, zoomScale };

View file

@ -0,0 +1,979 @@
/*!
* chartjs-plugin-zoom v2.2.0
* https://www.chartjs.org/chartjs-plugin-zoom/2.2.0/
* (c) 2016-2024 chartjs-plugin-zoom Contributors
* Released under the MIT License
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('chart.js'), require('hammerjs'), require('chart.js/helpers')) :
typeof define === 'function' && define.amd ? define(['chart.js', 'hammerjs', 'chart.js/helpers'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.ChartZoom = factory(global.Chart, global.Hammer, global.Chart.helpers));
})(this, (function (chart_js, Hammer, helpers) { 'use strict';
const getModifierKey = opts => opts && opts.enabled && opts.modifierKey;
const keyPressed = (key, event) => key && event[key + 'Key'];
const keyNotPressed = (key, event) => key && !event[key + 'Key'];
function directionEnabled(mode, dir, chart) {
if (mode === undefined) {
return true;
} else if (typeof mode === 'string') {
return mode.indexOf(dir) !== -1;
} else if (typeof mode === 'function') {
return mode({chart}).indexOf(dir) !== -1;
}
return false;
}
function directionsEnabled(mode, chart) {
if (typeof mode === 'function') {
mode = mode({chart});
}
if (typeof mode === 'string') {
return {x: mode.indexOf('x') !== -1, y: mode.indexOf('y') !== -1};
}
return {x: false, y: false};
}
function debounce(fn, delay) {
let timeout;
return function() {
clearTimeout(timeout);
timeout = setTimeout(fn, delay);
return delay;
};
}
function getScaleUnderPoint({x, y}, chart) {
const scales = chart.scales;
const scaleIds = Object.keys(scales);
for (let i = 0; i < scaleIds.length; i++) {
const scale = scales[scaleIds[i]];
if (y >= scale.top && y <= scale.bottom && x >= scale.left && x <= scale.right) {
return scale;
}
}
return null;
}
function getEnabledScalesByPoint(options, point, chart) {
const {mode = 'xy', scaleMode, overScaleMode} = options || {};
const scale = getScaleUnderPoint(point, chart);
const enabled = directionsEnabled(mode, chart);
const scaleEnabled = directionsEnabled(scaleMode, chart);
if (overScaleMode) {
const overScaleEnabled = directionsEnabled(overScaleMode, chart);
for (const axis of ['x', 'y']) {
if (overScaleEnabled[axis]) {
scaleEnabled[axis] = enabled[axis];
enabled[axis] = false;
}
}
}
if (scale && scaleEnabled[scale.axis]) {
return [scale];
}
const enabledScales = [];
helpers.each(chart.scales, function(scaleItem) {
if (enabled[scaleItem.axis]) {
enabledScales.push(scaleItem);
}
});
return enabledScales;
}
const chartStates = new WeakMap();
function getState(chart) {
let state = chartStates.get(chart);
if (!state) {
state = {
originalScaleLimits: {},
updatedScaleLimits: {},
handlers: {},
panDelta: {},
dragging: false,
panning: false
};
chartStates.set(chart, state);
}
return state;
}
function removeState(chart) {
chartStates.delete(chart);
}
function zoomDelta(val, min, range, newRange) {
const minPercent = Math.max(0, Math.min(1, (val - min) / range || 0));
const maxPercent = 1 - minPercent;
return {
min: newRange * minPercent,
max: newRange * maxPercent
};
}
function getValueAtPoint(scale, point) {
const pixel = scale.isHorizontal() ? point.x : point.y;
return scale.getValueForPixel(pixel);
}
function linearZoomDelta(scale, zoom, center) {
const range = scale.max - scale.min;
const newRange = range * (zoom - 1);
const centerValue = getValueAtPoint(scale, center);
return zoomDelta(centerValue, scale.min, range, newRange);
}
function logarithmicZoomRange(scale, zoom, center) {
const centerValue = getValueAtPoint(scale, center);
if (centerValue === undefined) {
return {min: scale.min, max: scale.max};
}
const logMin = Math.log10(scale.min);
const logMax = Math.log10(scale.max);
const logCenter = Math.log10(centerValue);
const logRange = logMax - logMin;
const newLogRange = logRange * (zoom - 1);
const delta = zoomDelta(logCenter, logMin, logRange, newLogRange);
return {
min: Math.pow(10, logMin + delta.min),
max: Math.pow(10, logMax - delta.max),
};
}
function getScaleLimits(scale, limits) {
return limits && (limits[scale.id] || limits[scale.axis]) || {};
}
function getLimit(state, scale, scaleLimits, prop, fallback) {
let limit = scaleLimits[prop];
if (limit === 'original') {
const original = state.originalScaleLimits[scale.id][prop];
limit = helpers.valueOrDefault(original.options, original.scale);
}
return helpers.valueOrDefault(limit, fallback);
}
function linearRange(scale, pixel0, pixel1) {
const v0 = scale.getValueForPixel(pixel0);
const v1 = scale.getValueForPixel(pixel1);
return {
min: Math.min(v0, v1),
max: Math.max(v0, v1)
};
}
function fixRange(range, {min, max, minLimit, maxLimit}, originalLimits) {
const offset = (range - max + min) / 2;
min -= offset;
max += offset;
const origMin = originalLimits.min.options ?? originalLimits.min.scale;
const origMax = originalLimits.max.options ?? originalLimits.max.scale;
const epsilon = range / 1e6;
if (helpers.almostEquals(min, origMin, epsilon)) {
min = origMin;
}
if (helpers.almostEquals(max, origMax, epsilon)) {
max = origMax;
}
if (min < minLimit) {
min = minLimit;
max = Math.min(minLimit + range, maxLimit);
} else if (max > maxLimit) {
max = maxLimit;
min = Math.max(maxLimit - range, minLimit);
}
return {min, max};
}
function updateRange(scale, {min, max}, limits, zoom = false) {
const state = getState(scale.chart);
const {options: scaleOpts} = scale;
const scaleLimits = getScaleLimits(scale, limits);
const {minRange = 0} = scaleLimits;
const minLimit = getLimit(state, scale, scaleLimits, 'min', -Infinity);
const maxLimit = getLimit(state, scale, scaleLimits, 'max', Infinity);
if (zoom === 'pan' && (min < minLimit || max > maxLimit)) {
return true;
}
const scaleRange = scale.max - scale.min;
const range = zoom ? Math.max(max - min, minRange) : scaleRange;
if (zoom && range === minRange && scaleRange <= minRange) {
return true;
}
const newRange = fixRange(range, {min, max, minLimit, maxLimit}, state.originalScaleLimits[scale.id]);
scaleOpts.min = newRange.min;
scaleOpts.max = newRange.max;
state.updatedScaleLimits[scale.id] = newRange;
return scale.parse(newRange.min) !== scale.min || scale.parse(newRange.max) !== scale.max;
}
function zoomNumericalScale(scale, zoom, center, limits) {
const delta = linearZoomDelta(scale, zoom, center);
const newRange = {min: scale.min + delta.min, max: scale.max - delta.max};
return updateRange(scale, newRange, limits, true);
}
function zoomLogarithmicScale(scale, zoom, center, limits) {
const newRange = logarithmicZoomRange(scale, zoom, center);
return updateRange(scale, newRange, limits, true);
}
function zoomRectNumericalScale(scale, from, to, limits) {
updateRange(scale, linearRange(scale, from, to), limits, true);
}
const integerChange = (v) => v === 0 || isNaN(v) ? 0 : v < 0 ? Math.min(Math.round(v), -1) : Math.max(Math.round(v), 1);
function existCategoryFromMaxZoom(scale) {
const labels = scale.getLabels();
const maxIndex = labels.length - 1;
if (scale.min > 0) {
scale.min -= 1;
}
if (scale.max < maxIndex) {
scale.max += 1;
}
}
function zoomCategoryScale(scale, zoom, center, limits) {
const delta = linearZoomDelta(scale, zoom, center);
if (scale.min === scale.max && zoom < 1) {
existCategoryFromMaxZoom(scale);
}
const newRange = {min: scale.min + integerChange(delta.min), max: scale.max - integerChange(delta.max)};
return updateRange(scale, newRange, limits, true);
}
function scaleLength(scale) {
return scale.isHorizontal() ? scale.width : scale.height;
}
function panCategoryScale(scale, delta, limits) {
const labels = scale.getLabels();
const lastLabelIndex = labels.length - 1;
let {min, max} = scale;
const range = Math.max(max - min, 1);
const stepDelta = Math.round(scaleLength(scale) / Math.max(range, 10));
const stepSize = Math.round(Math.abs(delta / stepDelta));
let applied;
if (delta < -stepDelta) {
max = Math.min(max + stepSize, lastLabelIndex);
min = range === 1 ? max : max - range;
applied = max === lastLabelIndex;
} else if (delta > stepDelta) {
min = Math.max(0, min - stepSize);
max = range === 1 ? min : min + range;
applied = min === 0;
}
return updateRange(scale, {min, max}, limits) || applied;
}
const OFFSETS = {
second: 500,
minute: 30 * 1000,
hour: 30 * 60 * 1000,
day: 12 * 60 * 60 * 1000,
week: 3.5 * 24 * 60 * 60 * 1000,
month: 15 * 24 * 60 * 60 * 1000,
quarter: 60 * 24 * 60 * 60 * 1000,
year: 182 * 24 * 60 * 60 * 1000
};
function panNumericalScale(scale, delta, limits, pan = false) {
const {min: prevStart, max: prevEnd, options} = scale;
const round = options.time && options.time.round;
const offset = OFFSETS[round] || 0;
const newMin = scale.getValueForPixel(scale.getPixelForValue(prevStart + offset) - delta);
const newMax = scale.getValueForPixel(scale.getPixelForValue(prevEnd + offset) - delta);
if (isNaN(newMin) || isNaN(newMax)) {
return true;
}
return updateRange(scale, {min: newMin, max: newMax}, limits, pan ? 'pan' : false);
}
function panNonLinearScale(scale, delta, limits) {
return panNumericalScale(scale, delta, limits, true);
}
const zoomFunctions = {
category: zoomCategoryScale,
default: zoomNumericalScale,
logarithmic: zoomLogarithmicScale,
};
const zoomRectFunctions = {
default: zoomRectNumericalScale,
};
const panFunctions = {
category: panCategoryScale,
default: panNumericalScale,
logarithmic: panNonLinearScale,
timeseries: panNonLinearScale,
};
function shouldUpdateScaleLimits(scale, originalScaleLimits, updatedScaleLimits) {
const {id, options: {min, max}} = scale;
if (!originalScaleLimits[id] || !updatedScaleLimits[id]) {
return true;
}
const previous = updatedScaleLimits[id];
return previous.min !== min || previous.max !== max;
}
function removeMissingScales(limits, scales) {
helpers.each(limits, (opt, key) => {
if (!scales[key]) {
delete limits[key];
}
});
}
function storeOriginalScaleLimits(chart, state) {
const {scales} = chart;
const {originalScaleLimits, updatedScaleLimits} = state;
helpers.each(scales, function(scale) {
if (shouldUpdateScaleLimits(scale, originalScaleLimits, updatedScaleLimits)) {
originalScaleLimits[scale.id] = {
min: {scale: scale.min, options: scale.options.min},
max: {scale: scale.max, options: scale.options.max},
};
}
});
removeMissingScales(originalScaleLimits, scales);
removeMissingScales(updatedScaleLimits, scales);
return originalScaleLimits;
}
function doZoom(scale, amount, center, limits) {
const fn = zoomFunctions[scale.type] || zoomFunctions.default;
helpers.callback(fn, [scale, amount, center, limits]);
}
function doZoomRect(scale, from, to, limits) {
const fn = zoomRectFunctions[scale.type] || zoomRectFunctions.default;
helpers.callback(fn, [scale, from, to, limits]);
}
function getCenter(chart) {
const ca = chart.chartArea;
return {
x: (ca.left + ca.right) / 2,
y: (ca.top + ca.bottom) / 2,
};
}
function zoom(chart, amount, transition = 'none', trigger = 'api') {
const {x = 1, y = 1, focalPoint = getCenter(chart)} = typeof amount === 'number' ? {x: amount, y: amount} : amount;
const state = getState(chart);
const {options: {limits, zoom: zoomOptions}} = state;
storeOriginalScaleLimits(chart, state);
const xEnabled = x !== 1;
const yEnabled = y !== 1;
const enabledScales = getEnabledScalesByPoint(zoomOptions, focalPoint, chart);
helpers.each(enabledScales || chart.scales, function(scale) {
if (scale.isHorizontal() && xEnabled) {
doZoom(scale, x, focalPoint, limits);
} else if (!scale.isHorizontal() && yEnabled) {
doZoom(scale, y, focalPoint, limits);
}
});
chart.update(transition);
helpers.callback(zoomOptions.onZoom, [{chart, trigger}]);
}
function zoomRect(chart, p0, p1, transition = 'none', trigger = 'api') {
const state = getState(chart);
const {options: {limits, zoom: zoomOptions}} = state;
const {mode = 'xy'} = zoomOptions;
storeOriginalScaleLimits(chart, state);
const xEnabled = directionEnabled(mode, 'x', chart);
const yEnabled = directionEnabled(mode, 'y', chart);
helpers.each(chart.scales, function(scale) {
if (scale.isHorizontal() && xEnabled) {
doZoomRect(scale, p0.x, p1.x, limits);
} else if (!scale.isHorizontal() && yEnabled) {
doZoomRect(scale, p0.y, p1.y, limits);
}
});
chart.update(transition);
helpers.callback(zoomOptions.onZoom, [{chart, trigger}]);
}
function zoomScale(chart, scaleId, range, transition = 'none', trigger = 'api') {
const state = getState(chart);
storeOriginalScaleLimits(chart, state);
const scale = chart.scales[scaleId];
updateRange(scale, range, undefined, true);
chart.update(transition);
helpers.callback(state.options.zoom?.onZoom, [{chart, trigger}]);
}
function resetZoom(chart, transition = 'default') {
const state = getState(chart);
const originalScaleLimits = storeOriginalScaleLimits(chart, state);
helpers.each(chart.scales, function(scale) {
const scaleOptions = scale.options;
if (originalScaleLimits[scale.id]) {
scaleOptions.min = originalScaleLimits[scale.id].min.options;
scaleOptions.max = originalScaleLimits[scale.id].max.options;
} else {
delete scaleOptions.min;
delete scaleOptions.max;
}
delete state.updatedScaleLimits[scale.id];
});
chart.update(transition);
helpers.callback(state.options.zoom.onZoomComplete, [{chart}]);
}
function getOriginalRange(state, scaleId) {
const original = state.originalScaleLimits[scaleId];
if (!original) {
return;
}
const {min, max} = original;
return helpers.valueOrDefault(max.options, max.scale) - helpers.valueOrDefault(min.options, min.scale);
}
function getZoomLevel(chart) {
const state = getState(chart);
let min = 1;
let max = 1;
helpers.each(chart.scales, function(scale) {
const origRange = getOriginalRange(state, scale.id);
if (origRange) {
const level = Math.round(origRange / (scale.max - scale.min) * 100) / 100;
min = Math.min(min, level);
max = Math.max(max, level);
}
});
return min < 1 ? min : max;
}
function panScale(scale, delta, limits, state) {
const {panDelta} = state;
const storedDelta = panDelta[scale.id] || 0;
if (helpers.sign(storedDelta) === helpers.sign(delta)) {
delta += storedDelta;
}
const fn = panFunctions[scale.type] || panFunctions.default;
if (helpers.callback(fn, [scale, delta, limits])) {
panDelta[scale.id] = 0;
} else {
panDelta[scale.id] = delta;
}
}
function pan(chart, delta, enabledScales, transition = 'none') {
const {x = 0, y = 0} = typeof delta === 'number' ? {x: delta, y: delta} : delta;
const state = getState(chart);
const {options: {pan: panOptions, limits}} = state;
const {onPan} = panOptions || {};
storeOriginalScaleLimits(chart, state);
const xEnabled = x !== 0;
const yEnabled = y !== 0;
helpers.each(enabledScales || chart.scales, function(scale) {
if (scale.isHorizontal() && xEnabled) {
panScale(scale, x, limits, state);
} else if (!scale.isHorizontal() && yEnabled) {
panScale(scale, y, limits, state);
}
});
chart.update(transition);
helpers.callback(onPan, [{chart}]);
}
function getInitialScaleBounds(chart) {
const state = getState(chart);
storeOriginalScaleLimits(chart, state);
const scaleBounds = {};
for (const scaleId of Object.keys(chart.scales)) {
const {min, max} = state.originalScaleLimits[scaleId] || {min: {}, max: {}};
scaleBounds[scaleId] = {min: min.scale, max: max.scale};
}
return scaleBounds;
}
function getZoomedScaleBounds(chart) {
const state = getState(chart);
const scaleBounds = {};
for (const scaleId of Object.keys(chart.scales)) {
scaleBounds[scaleId] = state.updatedScaleLimits[scaleId];
}
return scaleBounds;
}
function isZoomedOrPanned(chart) {
const scaleBounds = getInitialScaleBounds(chart);
for (const scaleId of Object.keys(chart.scales)) {
const {min: originalMin, max: originalMax} = scaleBounds[scaleId];
if (originalMin !== undefined && chart.scales[scaleId].min !== originalMin) {
return true;
}
if (originalMax !== undefined && chart.scales[scaleId].max !== originalMax) {
return true;
}
}
return false;
}
function isZoomingOrPanning(chart) {
const state = getState(chart);
return state.panning || state.dragging;
}
const clamp = (x, from, to) => Math.min(to, Math.max(from, x));
function removeHandler(chart, type) {
const {handlers} = getState(chart);
const handler = handlers[type];
if (handler && handler.target) {
handler.target.removeEventListener(type, handler);
delete handlers[type];
}
}
function addHandler(chart, target, type, handler) {
const {handlers, options} = getState(chart);
const oldHandler = handlers[type];
if (oldHandler && oldHandler.target === target) {
return;
}
removeHandler(chart, type);
handlers[type] = (event) => handler(chart, event, options);
handlers[type].target = target;
const passive = type === 'wheel' ? false : undefined;
target.addEventListener(type, handlers[type], {passive});
}
function mouseMove(chart, event) {
const state = getState(chart);
if (state.dragStart) {
state.dragging = true;
state.dragEnd = event;
chart.update('none');
}
}
function keyDown(chart, event) {
const state = getState(chart);
if (!state.dragStart || event.key !== 'Escape') {
return;
}
removeHandler(chart, 'keydown');
state.dragging = false;
state.dragStart = state.dragEnd = null;
chart.update('none');
}
function getPointPosition(event, chart) {
if (event.target !== chart.canvas) {
const canvasArea = chart.canvas.getBoundingClientRect();
return {
x: event.clientX - canvasArea.left,
y: event.clientY - canvasArea.top,
};
}
return helpers.getRelativePosition(event, chart);
}
function zoomStart(chart, event, zoomOptions) {
const {onZoomStart, onZoomRejected} = zoomOptions;
if (onZoomStart) {
const point = getPointPosition(event, chart);
if (helpers.callback(onZoomStart, [{chart, event, point}]) === false) {
helpers.callback(onZoomRejected, [{chart, event}]);
return false;
}
}
}
function mouseDown(chart, event) {
if (chart.legend) {
const point = helpers.getRelativePosition(event, chart);
if (helpers._isPointInArea(point, chart.legend)) {
return;
}
}
const state = getState(chart);
const {pan: panOptions, zoom: zoomOptions = {}} = state.options;
if (
event.button !== 0 ||
keyPressed(getModifierKey(panOptions), event) ||
keyNotPressed(getModifierKey(zoomOptions.drag), event)
) {
return helpers.callback(zoomOptions.onZoomRejected, [{chart, event}]);
}
if (zoomStart(chart, event, zoomOptions) === false) {
return;
}
state.dragStart = event;
addHandler(chart, chart.canvas.ownerDocument, 'mousemove', mouseMove);
addHandler(chart, window.document, 'keydown', keyDown);
}
function applyAspectRatio({begin, end}, aspectRatio) {
let width = end.x - begin.x;
let height = end.y - begin.y;
const ratio = Math.abs(width / height);
if (ratio > aspectRatio) {
width = Math.sign(width) * Math.abs(height * aspectRatio);
} else if (ratio < aspectRatio) {
height = Math.sign(height) * Math.abs(width / aspectRatio);
}
end.x = begin.x + width;
end.y = begin.y + height;
}
function applyMinMaxProps(rect, chartArea, points, {min, max, prop}) {
rect[min] = clamp(Math.min(points.begin[prop], points.end[prop]), chartArea[min], chartArea[max]);
rect[max] = clamp(Math.max(points.begin[prop], points.end[prop]), chartArea[min], chartArea[max]);
}
function getRelativePoints(chart, pointEvents, maintainAspectRatio) {
const points = {
begin: getPointPosition(pointEvents.dragStart, chart),
end: getPointPosition(pointEvents.dragEnd, chart),
};
if (maintainAspectRatio) {
const aspectRatio = chart.chartArea.width / chart.chartArea.height;
applyAspectRatio(points, aspectRatio);
}
return points;
}
function computeDragRect(chart, mode, pointEvents, maintainAspectRatio) {
const xEnabled = directionEnabled(mode, 'x', chart);
const yEnabled = directionEnabled(mode, 'y', chart);
const {top, left, right, bottom, width: chartWidth, height: chartHeight} = chart.chartArea;
const rect = {top, left, right, bottom};
const points = getRelativePoints(chart, pointEvents, maintainAspectRatio && xEnabled && yEnabled);
if (xEnabled) {
applyMinMaxProps(rect, chart.chartArea, points, {min: 'left', max: 'right', prop: 'x'});
}
if (yEnabled) {
applyMinMaxProps(rect, chart.chartArea, points, {min: 'top', max: 'bottom', prop: 'y'});
}
const width = rect.right - rect.left;
const height = rect.bottom - rect.top;
return {
...rect,
width,
height,
zoomX: xEnabled && width ? 1 + ((chartWidth - width) / chartWidth) : 1,
zoomY: yEnabled && height ? 1 + ((chartHeight - height) / chartHeight) : 1
};
}
function mouseUp(chart, event) {
const state = getState(chart);
if (!state.dragStart) {
return;
}
removeHandler(chart, 'mousemove');
const {mode, onZoomComplete, drag: {threshold = 0, maintainAspectRatio}} = state.options.zoom;
const rect = computeDragRect(chart, mode, {dragStart: state.dragStart, dragEnd: event}, maintainAspectRatio);
const distanceX = directionEnabled(mode, 'x', chart) ? rect.width : 0;
const distanceY = directionEnabled(mode, 'y', chart) ? rect.height : 0;
const distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
state.dragStart = state.dragEnd = null;
if (distance <= threshold) {
state.dragging = false;
chart.update('none');
return;
}
zoomRect(chart, {x: rect.left, y: rect.top}, {x: rect.right, y: rect.bottom}, 'zoom', 'drag');
state.dragging = false;
state.filterNextClick = true;
helpers.callback(onZoomComplete, [{chart}]);
}
function wheelPreconditions(chart, event, zoomOptions) {
if (keyNotPressed(getModifierKey(zoomOptions.wheel), event)) {
helpers.callback(zoomOptions.onZoomRejected, [{chart, event}]);
return;
}
if (zoomStart(chart, event, zoomOptions) === false) {
return;
}
if (event.cancelable) {
event.preventDefault();
}
if (event.deltaY === undefined) {
return;
}
return true;
}
function wheel(chart, event) {
const {handlers: {onZoomComplete}, options: {zoom: zoomOptions}} = getState(chart);
if (!wheelPreconditions(chart, event, zoomOptions)) {
return;
}
const rect = event.target.getBoundingClientRect();
const speed = zoomOptions.wheel.speed;
const percentage = event.deltaY >= 0 ? 2 - 1 / (1 - speed) : 1 + speed;
const amount = {
x: percentage,
y: percentage,
focalPoint: {
x: event.clientX - rect.left,
y: event.clientY - rect.top
}
};
zoom(chart, amount, 'zoom', 'wheel');
helpers.callback(onZoomComplete, [{chart}]);
}
function addDebouncedHandler(chart, name, handler, delay) {
if (handler) {
getState(chart).handlers[name] = debounce(() => helpers.callback(handler, [{chart}]), delay);
}
}
function addListeners(chart, options) {
const canvas = chart.canvas;
const {wheel: wheelOptions, drag: dragOptions, onZoomComplete} = options.zoom;
if (wheelOptions.enabled) {
addHandler(chart, canvas, 'wheel', wheel);
addDebouncedHandler(chart, 'onZoomComplete', onZoomComplete, 250);
} else {
removeHandler(chart, 'wheel');
}
if (dragOptions.enabled) {
addHandler(chart, canvas, 'mousedown', mouseDown);
addHandler(chart, canvas.ownerDocument, 'mouseup', mouseUp);
} else {
removeHandler(chart, 'mousedown');
removeHandler(chart, 'mousemove');
removeHandler(chart, 'mouseup');
removeHandler(chart, 'keydown');
}
}
function removeListeners(chart) {
removeHandler(chart, 'mousedown');
removeHandler(chart, 'mousemove');
removeHandler(chart, 'mouseup');
removeHandler(chart, 'wheel');
removeHandler(chart, 'click');
removeHandler(chart, 'keydown');
}
function createEnabler(chart, state) {
return function(recognizer, event) {
const {pan: panOptions, zoom: zoomOptions = {}} = state.options;
if (!panOptions || !panOptions.enabled) {
return false;
}
const srcEvent = event && event.srcEvent;
if (!srcEvent) {
return true;
}
if (!state.panning && event.pointerType === 'mouse' && (
keyNotPressed(getModifierKey(panOptions), srcEvent) || keyPressed(getModifierKey(zoomOptions.drag), srcEvent))
) {
helpers.callback(panOptions.onPanRejected, [{chart, event}]);
return false;
}
return true;
};
}
function pinchAxes(p0, p1) {
const pinchX = Math.abs(p0.clientX - p1.clientX);
const pinchY = Math.abs(p0.clientY - p1.clientY);
const p = pinchX / pinchY;
let x, y;
if (p > 0.3 && p < 1.7) {
x = y = true;
} else if (pinchX > pinchY) {
x = true;
} else {
y = true;
}
return {x, y};
}
function handlePinch(chart, state, e) {
if (state.scale) {
const {center, pointers} = e;
const zoomPercent = 1 / state.scale * e.scale;
const rect = e.target.getBoundingClientRect();
const pinch = pinchAxes(pointers[0], pointers[1]);
const mode = state.options.zoom.mode;
const amount = {
x: pinch.x && directionEnabled(mode, 'x', chart) ? zoomPercent : 1,
y: pinch.y && directionEnabled(mode, 'y', chart) ? zoomPercent : 1,
focalPoint: {
x: center.x - rect.left,
y: center.y - rect.top
}
};
zoom(chart, amount, 'zoom', 'pinch');
state.scale = e.scale;
}
}
function startPinch(chart, state, event) {
if (state.options.zoom.pinch.enabled) {
const point = helpers.getRelativePosition(event, chart);
if (helpers.callback(state.options.zoom.onZoomStart, [{chart, event, point}]) === false) {
state.scale = null;
helpers.callback(state.options.zoom.onZoomRejected, [{chart, event}]);
} else {
state.scale = 1;
}
}
}
function endPinch(chart, state, e) {
if (state.scale) {
handlePinch(chart, state, e);
state.scale = null;
helpers.callback(state.options.zoom.onZoomComplete, [{chart}]);
}
}
function handlePan(chart, state, e) {
const delta = state.delta;
if (delta) {
state.panning = true;
pan(chart, {x: e.deltaX - delta.x, y: e.deltaY - delta.y}, state.panScales);
state.delta = {x: e.deltaX, y: e.deltaY};
}
}
function startPan(chart, state, event) {
const {enabled, onPanStart, onPanRejected} = state.options.pan;
if (!enabled) {
return;
}
const rect = event.target.getBoundingClientRect();
const point = {
x: event.center.x - rect.left,
y: event.center.y - rect.top
};
if (helpers.callback(onPanStart, [{chart, event, point}]) === false) {
return helpers.callback(onPanRejected, [{chart, event}]);
}
state.panScales = getEnabledScalesByPoint(state.options.pan, point, chart);
state.delta = {x: 0, y: 0};
handlePan(chart, state, event);
}
function endPan(chart, state) {
state.delta = null;
if (state.panning) {
state.panning = false;
state.filterNextClick = true;
helpers.callback(state.options.pan.onPanComplete, [{chart}]);
}
}
const hammers = new WeakMap();
function startHammer(chart, options) {
const state = getState(chart);
const canvas = chart.canvas;
const {pan: panOptions, zoom: zoomOptions} = options;
const mc = new Hammer.Manager(canvas);
if (zoomOptions && zoomOptions.pinch.enabled) {
mc.add(new Hammer.Pinch());
mc.on('pinchstart', (e) => startPinch(chart, state, e));
mc.on('pinch', (e) => handlePinch(chart, state, e));
mc.on('pinchend', (e) => endPinch(chart, state, e));
}
if (panOptions && panOptions.enabled) {
mc.add(new Hammer.Pan({
threshold: panOptions.threshold,
enable: createEnabler(chart, state)
}));
mc.on('panstart', (e) => startPan(chart, state, e));
mc.on('panmove', (e) => handlePan(chart, state, e));
mc.on('panend', () => endPan(chart, state));
}
hammers.set(chart, mc);
}
function stopHammer(chart) {
const mc = hammers.get(chart);
if (mc) {
mc.remove('pinchstart');
mc.remove('pinch');
mc.remove('pinchend');
mc.remove('panstart');
mc.remove('pan');
mc.remove('panend');
mc.destroy();
hammers.delete(chart);
}
}
function hammerOptionsChanged(oldOptions, newOptions) {
const {pan: oldPan, zoom: oldZoom} = oldOptions;
const {pan: newPan, zoom: newZoom} = newOptions;
if (oldZoom?.zoom?.pinch?.enabled !== newZoom?.zoom?.pinch?.enabled) {
return true;
}
if (oldPan?.enabled !== newPan?.enabled) {
return true;
}
if (oldPan?.threshold !== newPan?.threshold) {
return true;
}
return false;
}
var version = "2.2.0";
function draw(chart, caller, options) {
const dragOptions = options.zoom.drag;
const {dragStart, dragEnd} = getState(chart);
if (dragOptions.drawTime !== caller || !dragEnd) {
return;
}
const {left, top, width, height} = computeDragRect(chart, options.zoom.mode, {dragStart, dragEnd}, dragOptions.maintainAspectRatio);
const ctx = chart.ctx;
ctx.save();
ctx.beginPath();
ctx.fillStyle = dragOptions.backgroundColor || 'rgba(225,225,225,0.3)';
ctx.fillRect(left, top, width, height);
if (dragOptions.borderWidth > 0) {
ctx.lineWidth = dragOptions.borderWidth;
ctx.strokeStyle = dragOptions.borderColor || 'rgba(225,225,225)';
ctx.strokeRect(left, top, width, height);
}
ctx.restore();
}
var Zoom = {
id: 'zoom',
version,
defaults: {
pan: {
enabled: false,
mode: 'xy',
threshold: 10,
modifierKey: null,
},
zoom: {
wheel: {
enabled: false,
speed: 0.1,
modifierKey: null
},
drag: {
enabled: false,
drawTime: 'beforeDatasetsDraw',
modifierKey: null
},
pinch: {
enabled: false
},
mode: 'xy',
}
},
start: function(chart, _args, options) {
const state = getState(chart);
state.options = options;
if (Object.prototype.hasOwnProperty.call(options.zoom, 'enabled')) {
console.warn('The option `zoom.enabled` is no longer supported. Please use `zoom.wheel.enabled`, `zoom.drag.enabled`, or `zoom.pinch.enabled`.');
}
if (Object.prototype.hasOwnProperty.call(options.zoom, 'overScaleMode')
|| Object.prototype.hasOwnProperty.call(options.pan, 'overScaleMode')) {
console.warn('The option `overScaleMode` is deprecated. Please use `scaleMode` instead (and update `mode` as desired).');
}
if (Hammer) {
startHammer(chart, options);
}
chart.pan = (delta, panScales, transition) => pan(chart, delta, panScales, transition);
chart.zoom = (args, transition) => zoom(chart, args, transition);
chart.zoomRect = (p0, p1, transition) => zoomRect(chart, p0, p1, transition);
chart.zoomScale = (id, range, transition) => zoomScale(chart, id, range, transition);
chart.resetZoom = (transition) => resetZoom(chart, transition);
chart.getZoomLevel = () => getZoomLevel(chart);
chart.getInitialScaleBounds = () => getInitialScaleBounds(chart);
chart.getZoomedScaleBounds = () => getZoomedScaleBounds(chart);
chart.isZoomedOrPanned = () => isZoomedOrPanned(chart);
chart.isZoomingOrPanning = () => isZoomingOrPanning(chart);
},
beforeEvent(chart, {event}) {
if (isZoomingOrPanning(chart)) {
return false;
}
if (event.type === 'click' || event.type === 'mouseup') {
const state = getState(chart);
if (state.filterNextClick) {
state.filterNextClick = false;
return false;
}
}
},
beforeUpdate: function(chart, args, options) {
const state = getState(chart);
const previousOptions = state.options;
state.options = options;
if (hammerOptionsChanged(previousOptions, options)) {
stopHammer(chart);
startHammer(chart, options);
}
addListeners(chart, options);
},
beforeDatasetsDraw(chart, _args, options) {
draw(chart, 'beforeDatasetsDraw', options);
},
afterDatasetsDraw(chart, _args, options) {
draw(chart, 'afterDatasetsDraw', options);
},
beforeDraw(chart, _args, options) {
draw(chart, 'beforeDraw', options);
},
afterDraw(chart, _args, options) {
draw(chart, 'afterDraw', options);
},
stop: function(chart) {
removeListeners(chart);
if (Hammer) {
stopHammer(chart);
}
removeState(chart);
},
panFunctions,
zoomFunctions,
zoomRectFunctions,
};
chart_js.Chart.register(Zoom);
return Zoom;
}));

File diff suppressed because one or more lines are too long

90
node_modules/chartjs-plugin-zoom/package.json generated vendored Normal file
View file

@ -0,0 +1,90 @@
{
"name": "chartjs-plugin-zoom",
"homepage": "https://www.chartjs.org/chartjs-plugin-zoom/",
"description": "Plugin that enables zoom and pan functionality in Chart.js charts.",
"version": "2.2.0",
"license": "MIT",
"jsdelivr": "dist/chartjs-plugin-zoom.min.js",
"unpkg": "dist/chartjs-plugin-zoom.min.js",
"main": "dist/chartjs-plugin-zoom.js",
"module": "dist/chartjs-plugin-zoom.esm.js",
"types": "types/index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/chartjs/chartjs-plugin-zoom.git"
},
"scripts": {
"autobuild": "rollup -c -w",
"build": "rollup -c",
"dev": "karma start --auto-watch --no-single-run --browsers chrome",
"dev:ff": "karma start --auto-watch --no-single-run --browsers firefox",
"docs": "npm run build && vuepress build docs --no-cache",
"docs:dev": "concurrently \"npm run autobuild\" \"vuepress dev docs --no-cache\"",
"lint-js": "eslint \"samples/**/*.html\" \"test/**/*.js\" \"src/**/*.js\"",
"lint-md": "eslint \"**/*.md\"",
"lint-tsc": "tsc",
"lint-types": "eslint \"types/**/*.ts\" && tsc -p types/tests/",
"lint": "concurrently \"npm:lint-*\"",
"test": "cross-env NODE_ENV=test concurrently \"npm:test-*\"",
"test-karma": "karma start --auto-watch --single-run --coverage",
"test-types": "tsc -p types/tests/"
},
"files": [
"dist/*.js",
"types/*.d.ts"
],
"devDependencies": {
"@babel/core": "^7.20.2",
"@babel/preset-env": "^7.20.2",
"@rollup/plugin-commonjs": "^28.0.1",
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-terser": "^0.4.4",
"@simonbrunel/vuepress-plugin-versions": "^0.2.0",
"@types/linkify-it": "^3.0.5",
"@typescript-eslint/eslint-plugin": "^5.4.0",
"@typescript-eslint/parser": "^5.4.0",
"babel-loader": "^8.3.0",
"chart.js": "^4.3.2",
"chartjs-adapter-date-fns": "^3.0.0",
"chartjs-test-utils": "^0.5.0",
"concurrently": "^9.1.0",
"coveralls": "^3.1.0",
"cross-env": "^7.0.3",
"date-fns": "^2.21.1",
"eslint": "^8.2.0",
"eslint-config-chartjs": "^0.3.0",
"eslint-plugin-html": "^8.1.2",
"eslint-plugin-markdown": "^2.0.1",
"hammer-simulator": "^0.0.1",
"jasmine": "^5.4.0",
"karma": "^6.4.4",
"karma-chrome-launcher": "^3.2.0",
"karma-coverage": "^2.2.1",
"karma-firefox-launcher": "^2.1.3",
"karma-jasmine": "^5.1.0",
"karma-jasmine-html-reporter": "^2.1.0",
"karma-rollup-preprocessor": "7.0.7",
"karma-spec-reporter": "0.0.36",
"ng-hammerjs": "^2.0.8",
"pixelmatch": "^6.0.0",
"rollup": "^4.12.1",
"rollup-plugin-cleanup": "^3.2.1",
"rollup-plugin-istanbul": "^5.0.0",
"typedoc": "^0.26.11",
"typedoc-plugin-markdown": "^4.2.10",
"typescript": "^5.6.3",
"vuepress": "^1.8.2",
"vuepress-plugin-flexsearch": "^0.3.0",
"vuepress-plugin-redirect": "^1.2.5",
"vuepress-plugin-typedoc": "^0.9.2",
"vuepress-theme-chartjs": "^0.2.0"
},
"peerDependencies": {
"chart.js": ">=3.2.0"
},
"dependencies": {
"@types/hammerjs": "^2.0.45",
"hammerjs": "^2.0.8"
}
}

63
node_modules/chartjs-plugin-zoom/types/index.d.ts generated vendored Normal file
View file

@ -0,0 +1,63 @@
import { Plugin, ChartType, Chart, Scale, UpdateMode, ScaleTypeRegistry, ChartTypeRegistry } from 'chart.js';
import { LimitOptions, ZoomPluginOptions } from './options';
type Point = { x: number, y: number };
type DistributiveArray<T> = [T] extends [unknown] ? Array<T> : never
export type PanAmount = number | Partial<Point>;
export type ScaleRange = { min: number, max: number };
export type ZoomAmount = number | Partial<Point> & { focalPoint?: Point };
declare module 'chart.js' {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface PluginOptionsByType<TType extends ChartType> {
zoom: ZoomPluginOptions;
}
enum UpdateModeEnum {
zoom = 'zoom'
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface Chart<TType extends keyof ChartTypeRegistry = keyof ChartTypeRegistry, TData = DistributiveArray<ChartTypeRegistry[TType]['defaultDataPoint']>, TLabel = unknown> {
pan(pan: PanAmount, scales?: Scale[], mode?: UpdateMode): void;
zoom(zoom: ZoomAmount, mode?: UpdateMode): void;
zoomRect(p0: Point, p1: Point, mode?: UpdateMode): void;
zoomScale(id: string, range: ScaleRange, mode?: UpdateMode): void;
resetZoom(mode?: UpdateMode): void;
getZoomLevel(): number;
getInitialScaleBounds(): Record<string, {min: number | undefined, max: number | undefined}>;
getZoomedScaleBounds(): Record<string, ScaleRange | undefined>;
isZoomedOrPanned(): boolean;
isZoomingOrPanning(): boolean;
}
}
export type ZoomFunction = (scale: Scale, zoom: number, center: Point, limits: LimitOptions) => boolean;
export type ZoomRectFunction = (scale: Scale, from: number, to: number, limits: LimitOptions) => boolean;
export type PanFunction = (scale: Scale, delta: number, limits: LimitOptions) => boolean;
type ScaleFunctions<T> = {
[scaleType in keyof ScaleTypeRegistry]?: T | undefined;
} & {
default: T;
};
declare const Zoom: Plugin & {
zoomFunctions: ScaleFunctions<ZoomFunction>;
zoomRectFunctions: ScaleFunctions<ZoomRectFunction>;
panFunctions: ScaleFunctions<PanFunction>;
};
export default Zoom;
export function pan(chart: Chart, amount: PanAmount, scales?: Scale[], mode?: UpdateMode): void;
export function zoom(chart: Chart, amount: ZoomAmount, mode?: UpdateMode): void;
export function zoomRect(chart: Chart, p0: Point, p1: Point, mode?: UpdateMode): void;
export function zoomScale(chart: Chart, scaleId: string, range: ScaleRange, mode?: UpdateMode): void;
export function resetZoom(chart: Chart, mode?: UpdateMode): void;
export function getZoomLevel(chart: Chart): number;
export function getInitialScaleBounds(chart: Chart): Record<string, {min: number | undefined, max: number | undefined}>;
export function getZoomedScaleBounds(chart: Chart): Record<string, ScaleRange | undefined>;
export function isZoomedOrPanned(chart: Chart): boolean;
export function isZoomingOrPanning(chart: Chart): boolean;

196
node_modules/chartjs-plugin-zoom/types/options.d.ts generated vendored Normal file
View file

@ -0,0 +1,196 @@
import { Chart, Color, Point } from 'chart.js';
import { Input as HammerInput } from 'hammerjs';
export type Mode = 'x' | 'y' | 'xy';
export type ModifierKey = 'ctrl' | 'alt' | 'shift' | 'meta';
export type DrawTime = 'afterDraw' | 'afterDatasetsDraw' | 'beforeDraw' | 'beforeDatasetsDraw';
export type ZoomTrigger = 'api' | 'drag' | 'wheel' | 'pinch'
export interface WheelOptions {
/**
* Enable the zoom via mouse wheel
*/
enabled?: boolean;
/**
* Speed of zoom via mouse wheel
* (percentage of zoom on a wheel event)
*/
speed?: number;
/**
* Modifier key required for zooming with mouse
*/
modifierKey?: ModifierKey;
}
export interface DragOptions {
/**
* Enable the zoom via drag
*/
enabled?: boolean;
/**
* Minimal zoom distance required before actually applying zoom
*/
threshold?: number;
/**
* Border color of the drag area
*/
borderColor?: Color;
/**
* Border width of the drag area
*/
borderWidth?: number;
/**
* Background color of the drag area
*/
backgroundColor?: Color;
/**
* Modifier key required for drag-to-zoom
*/
modifierKey?: ModifierKey;
/**
* Draw time required for drag-to-zoom
*/
drawTime?: DrawTime;
/**
* Maintain aspect ratio of the drag rectangle
*/
maintainAspectRatio?: boolean;
}
export interface PinchOptions {
/**
* Enable the zoom via pinch
*/
enabled?: boolean;
}
/**
* Container for zoom options
*/
export interface ZoomOptions {
/**
* Zooming directions. Remove the appropriate direction to disable
* E.g. 'y' would only allow zooming in the y direction
* A function that is called as the user is zooming and returns the
* available directions can also be used:
* mode: function({ chart }) {
* return 'xy';
* },
*/
mode?: Mode | { (chart: Chart): Mode };
/**
* Options of the mouse wheel mode
*/
wheel?: WheelOptions;
/**
* Options of the drag-to-zoom mode
*/
drag?: DragOptions;
/**
* Options of the pinch mode
*/
pinch?: PinchOptions;
scaleMode?: Mode | { (chart: Chart): Mode };
/** @deprecated Use scaleMode instead */
overScaleMode?: Mode | { (chart: Chart): Mode };
/**
* Function called while the user is zooming
*/
onZoom?: (context: { chart: Chart, trigger: ZoomTrigger }) => void;
/**
* Function called once zooming is completed
*/
onZoomComplete?: (context: { chart: Chart }) => void;
/**
* Function called when wheel input occurs without modifier key
*/
onZoomRejected?: (context: { chart: Chart, event: Event }) => void;
onZoomStart?: (context: { chart: Chart, event: Event, point: Point }) => boolean | undefined;
}
/**
* Container for pan options
*/
export interface PanOptions {
/**
* Boolean to enable panning
*/
enabled?: boolean;
/**
* Panning directions. Remove the appropriate direction to disable
* E.g. 'y' would only allow panning in the y direction
* A function that is called as the user is panning and returns the
* available directions can also be used:
* mode: function({ chart }) {
* return 'xy';
* },
*/
mode?: Mode | { (chart: Chart): Mode };
/**
* Modifier key required for panning with mouse
*/
modifierKey?: ModifierKey;
scaleMode?: Mode | { (chart: Chart): Mode };
/** @deprecated Use scaleMode instead */
overScaleMode?: Mode | { (chart: Chart): Mode };
/**
* Minimal pan distance required before actually applying pan
*/
threshold?: number;
/**
* Function called while the user is panning
*/
onPan?: (context: { chart: Chart }) => void;
/**
* Function called once panning is completed
*/
onPanComplete?: (context: { chart: Chart }) => void;
/**
* Function called when pan fails because modifier key was not detected.
* event is the Hammer event that failed - see https://hammerjs.github.io/api#event-object
*/
onPanRejected?: (context: { chart: Chart, event: HammerInput }) => void;
onPanStart?: (context: { chart: Chart, event: HammerInput, point: Point }) => boolean | undefined;
}
export interface ScaleLimits {
min?: number | 'original';
max?: number | 'original';
minRange?: number;
}
export interface LimitOptions {
// Scale limits, indexed by the scale's ID (key) or by axis (x/y)
[axisId: string]: ScaleLimits;
}
export interface ZoomPluginOptions {
pan?: PanOptions;
limits?: LimitOptions;
zoom?: ZoomOptions;
}