feat: Passwordless cross-device authentication

- Arkitektur: docs/auth/passwordless-architecture.md
- Backend: iom/quixzoom-auth-service/ (FastAPI + Redis)
- Webb: quixzoom-market-pages/se/login/ (QR-kod + polling)
- App: iom/quixzoom-app/src/features/auth/ (push + deep links)

Flöde: QR-kod → app-godkännande → webb-inloggad
This commit is contained in:
Bernt
2026-07-07 07:11:50 +00:00
parent 4aa984ad74
commit 6989a98d75
61843 changed files with 5491611 additions and 872231 deletions
+9
View File
@@ -0,0 +1,9 @@
The MIT License (MIT)
Copyright (c) 2014-2024 Chart.js 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.
+38
View File
@@ -0,0 +1,38 @@
<p align="center">
<a href="https://www.chartjs.org/" target="_blank">
<img src="https://www.chartjs.org/media/logo-title.svg" alt="https://www.chartjs.org/"><br/>
</a>
Simple yet flexible JavaScript charting for designers & developers
</p>
<p align="center">
<a href="https://www.chartjs.org/docs/latest/getting-started/installation.html"><img src="https://img.shields.io/github/release/chartjs/Chart.js.svg?style=flat-square&maxAge=600" alt="Downloads"></a>
<a href="https://github.com/chartjs/Chart.js/actions?query=workflow%3ACI+branch%3Amaster"><img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/chartjs/Chart.js/ci.yml?branch=master&style=flat-square"></a>
<a href="https://coveralls.io/github/chartjs/Chart.js?branch=master"><img src="https://img.shields.io/coveralls/chartjs/Chart.js.svg?style=flat-square&maxAge=600" alt="Coverage"></a>
<a href="https://github.com/chartjs/awesome"><img src="https://awesome.re/badge-flat2.svg" alt="Awesome"></a>
<a href="https://discord.gg/HxEguTK6av"><img src="https://img.shields.io/badge/discord-chartjs-blue?style=flat-square&maxAge=3600" alt="Discord"></a>
</p>
## Documentation
All the links point to the new version 4 of the lib.
* [Introduction](https://www.chartjs.org/docs/latest/)
* [Getting Started](https://www.chartjs.org/docs/latest/getting-started/index)
* [General](https://www.chartjs.org/docs/latest/general/data-structures)
* [Configuration](https://www.chartjs.org/docs/latest/configuration/index)
* [Charts](https://www.chartjs.org/docs/latest/charts/line)
* [Axes](https://www.chartjs.org/docs/latest/axes/index)
* [Developers](https://www.chartjs.org/docs/latest/developers/index)
* [Popular Extensions](https://github.com/chartjs/awesome)
* [Samples](https://www.chartjs.org/samples/)
In case you are looking for an older version of the docs, you will have to specify the specific version in the url like this: [https://www.chartjs.org/docs/2.9.4/](https://www.chartjs.org/docs/2.9.4/)
## Contributing
Instructions on building and testing Chart.js can be found in [the documentation](https://www.chartjs.org/docs/master/developers/contributing.html#building-and-testing). Before submitting an issue or a pull request, please take a moment to look over the [contributing guidelines](https://www.chartjs.org/docs/master/developers/contributing) first. For support, please post questions on [Stack Overflow](https://stackoverflow.com/questions/tagged/chart.js) with the `chart.js` tag.
## License
Chart.js is available under the [MIT license](LICENSE.md).
+6
View File
@@ -0,0 +1,6 @@
const chartjs = require('../dist/chart.cjs');
const {Chart, registerables} = chartjs;
Chart.register(...registerables);
module.exports = Object.assign(Chart, chartjs);
+4
View File
@@ -0,0 +1,4 @@
import {Chart} from '../dist/types.js';
export * from '../dist/types.js';
export default Chart;
+6
View File
@@ -0,0 +1,6 @@
import {Chart, registerables} from '../dist/chart.js';
Chart.register(...registerables);
export * from '../dist/chart.js';
export default Chart;
+14
View File
@@ -0,0 +1,14 @@
{
"name": "chart.js-auto",
"private": true,
"description": "Auto registering package. Exists to support bundlers without exports support such as webpack 4.",
"type": "module",
"main": "./auto.cjs",
"module": "./auto.js",
"exports": {
"types": "./auto.d.ts",
"import": "./auto.js",
"require": "./auto.cjs"
},
"types": "./auto.d.ts"
}
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2915
View File
@@ -0,0 +1,2915 @@
/*!
* Chart.js v4.5.1
* https://www.chartjs.org
* (c) 2025 Chart.js Contributors
* Released under the MIT License
*/
'use strict';
var color$1 = require('@kurkle/color');
/**
* @namespace Chart.helpers
*/ /**
* An empty function that can be used, for example, for optional callback.
*/ function noop() {
/* noop */ }
/**
* Returns a unique id, sequentially generated from a global variable.
*/ const uid = (()=>{
let id = 0;
return ()=>id++;
})();
/**
* Returns true if `value` is neither null nor undefined, else returns false.
* @param value - The value to test.
* @since 2.7.0
*/ function isNullOrUndef(value) {
return value === null || value === undefined;
}
/**
* Returns true if `value` is an array (including typed arrays), else returns false.
* @param value - The value to test.
* @function
*/ function isArray(value) {
if (Array.isArray && Array.isArray(value)) {
return true;
}
const type = Object.prototype.toString.call(value);
if (type.slice(0, 7) === '[object' && type.slice(-6) === 'Array]') {
return true;
}
return false;
}
/**
* Returns true if `value` is an object (excluding null), else returns false.
* @param value - The value to test.
* @since 2.7.0
*/ function isObject(value) {
return value !== null && Object.prototype.toString.call(value) === '[object Object]';
}
/**
* Returns true if `value` is a finite number, else returns false
* @param value - The value to test.
*/ function isNumberFinite(value) {
return (typeof value === 'number' || value instanceof Number) && isFinite(+value);
}
/**
* Returns `value` if finite, else returns `defaultValue`.
* @param value - The value to return if defined.
* @param defaultValue - The value to return if `value` is not finite.
*/ function finiteOrDefault(value, defaultValue) {
return isNumberFinite(value) ? value : defaultValue;
}
/**
* Returns `value` if defined, else returns `defaultValue`.
* @param value - The value to return if defined.
* @param defaultValue - The value to return if `value` is undefined.
*/ function valueOrDefault(value, defaultValue) {
return typeof value === 'undefined' ? defaultValue : value;
}
const toPercentage = (value, dimension)=>typeof value === 'string' && value.endsWith('%') ? parseFloat(value) / 100 : +value / dimension;
const toDimension = (value, dimension)=>typeof value === 'string' && value.endsWith('%') ? parseFloat(value) / 100 * dimension : +value;
/**
* Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the
* value returned by `fn`. If `fn` is not a function, this method returns undefined.
* @param fn - The function to call.
* @param args - The arguments with which `fn` should be called.
* @param [thisArg] - The value of `this` provided for the call to `fn`.
*/ function callback(fn, args, thisArg) {
if (fn && typeof fn.call === 'function') {
return fn.apply(thisArg, args);
}
}
function each(loopable, fn, thisArg, reverse) {
let i, len, keys;
if (isArray(loopable)) {
len = loopable.length;
if (reverse) {
for(i = len - 1; i >= 0; i--){
fn.call(thisArg, loopable[i], i);
}
} else {
for(i = 0; i < len; i++){
fn.call(thisArg, loopable[i], i);
}
}
} else if (isObject(loopable)) {
keys = Object.keys(loopable);
len = keys.length;
for(i = 0; i < len; i++){
fn.call(thisArg, loopable[keys[i]], keys[i]);
}
}
}
/**
* Returns true if the `a0` and `a1` arrays have the same content, else returns false.
* @param a0 - The array to compare
* @param a1 - The array to compare
* @private
*/ function _elementsEqual(a0, a1) {
let i, ilen, v0, v1;
if (!a0 || !a1 || a0.length !== a1.length) {
return false;
}
for(i = 0, ilen = a0.length; i < ilen; ++i){
v0 = a0[i];
v1 = a1[i];
if (v0.datasetIndex !== v1.datasetIndex || v0.index !== v1.index) {
return false;
}
}
return true;
}
/**
* Returns a deep copy of `source` without keeping references on objects and arrays.
* @param source - The value to clone.
*/ function clone(source) {
if (isArray(source)) {
return source.map(clone);
}
if (isObject(source)) {
const target = Object.create(null);
const keys = Object.keys(source);
const klen = keys.length;
let k = 0;
for(; k < klen; ++k){
target[keys[k]] = clone(source[keys[k]]);
}
return target;
}
return source;
}
function isValidKey(key) {
return [
'__proto__',
'prototype',
'constructor'
].indexOf(key) === -1;
}
/**
* The default merger when Chart.helpers.merge is called without merger option.
* Note(SB): also used by mergeConfig and mergeScaleConfig as fallback.
* @private
*/ function _merger(key, target, source, options) {
if (!isValidKey(key)) {
return;
}
const tval = target[key];
const sval = source[key];
if (isObject(tval) && isObject(sval)) {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
merge(tval, sval, options);
} else {
target[key] = clone(sval);
}
}
function merge(target, source, options) {
const sources = isArray(source) ? source : [
source
];
const ilen = sources.length;
if (!isObject(target)) {
return target;
}
options = options || {};
const merger = options.merger || _merger;
let current;
for(let i = 0; i < ilen; ++i){
current = sources[i];
if (!isObject(current)) {
continue;
}
const keys = Object.keys(current);
for(let k = 0, klen = keys.length; k < klen; ++k){
merger(keys[k], target, current, options);
}
}
return target;
}
function mergeIf(target, source) {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
return merge(target, source, {
merger: _mergerIf
});
}
/**
* Merges source[key] in target[key] only if target[key] is undefined.
* @private
*/ function _mergerIf(key, target, source) {
if (!isValidKey(key)) {
return;
}
const tval = target[key];
const sval = source[key];
if (isObject(tval) && isObject(sval)) {
mergeIf(tval, sval);
} else if (!Object.prototype.hasOwnProperty.call(target, key)) {
target[key] = clone(sval);
}
}
/**
* @private
*/ function _deprecated(scope, value, previous, current) {
if (value !== undefined) {
console.warn(scope + ': "' + previous + '" is deprecated. Please use "' + current + '" instead');
}
}
// resolveObjectKey resolver cache
const keyResolvers = {
// Chart.helpers.core resolveObjectKey should resolve empty key to root object
'': (v)=>v,
// default resolvers
x: (o)=>o.x,
y: (o)=>o.y
};
/**
* @private
*/ function _splitKey(key) {
const parts = key.split('.');
const keys = [];
let tmp = '';
for (const part of parts){
tmp += part;
if (tmp.endsWith('\\')) {
tmp = tmp.slice(0, -1) + '.';
} else {
keys.push(tmp);
tmp = '';
}
}
return keys;
}
function _getKeyResolver(key) {
const keys = _splitKey(key);
return (obj)=>{
for (const k of keys){
if (k === '') {
break;
}
obj = obj && obj[k];
}
return obj;
};
}
function resolveObjectKey(obj, key) {
const resolver = keyResolvers[key] || (keyResolvers[key] = _getKeyResolver(key));
return resolver(obj);
}
/**
* @private
*/ function _capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
const defined = (value)=>typeof value !== 'undefined';
const isFunction = (value)=>typeof value === 'function';
// Adapted from https://stackoverflow.com/questions/31128855/comparing-ecma6-sets-for-equality#31129384
const setsEqual = (a, b)=>{
if (a.size !== b.size) {
return false;
}
for (const item of a){
if (!b.has(item)) {
return false;
}
}
return true;
};
/**
* @param e - The event
* @private
*/ function _isClickEvent(e) {
return e.type === 'mouseup' || e.type === 'click' || e.type === 'contextmenu';
}
/**
* @alias Chart.helpers.math
* @namespace
*/ const PI = Math.PI;
const TAU = 2 * PI;
const PITAU = TAU + PI;
const INFINITY = Number.POSITIVE_INFINITY;
const RAD_PER_DEG = PI / 180;
const HALF_PI = PI / 2;
const QUARTER_PI = PI / 4;
const TWO_THIRDS_PI = PI * 2 / 3;
const log10 = Math.log10;
const sign = Math.sign;
function almostEquals(x, y, epsilon) {
return Math.abs(x - y) < epsilon;
}
/**
* Implementation of the nice number algorithm used in determining where axis labels will go
*/ function niceNum(range) {
const roundedRange = Math.round(range);
range = almostEquals(range, roundedRange, range / 1000) ? roundedRange : range;
const niceRange = Math.pow(10, Math.floor(log10(range)));
const fraction = range / niceRange;
const niceFraction = fraction <= 1 ? 1 : fraction <= 2 ? 2 : fraction <= 5 ? 5 : 10;
return niceFraction * niceRange;
}
/**
* Returns an array of factors sorted from 1 to sqrt(value)
* @private
*/ function _factorize(value) {
const result = [];
const sqrt = Math.sqrt(value);
let i;
for(i = 1; i < sqrt; i++){
if (value % i === 0) {
result.push(i);
result.push(value / i);
}
}
if (sqrt === (sqrt | 0)) {
result.push(sqrt);
}
result.sort((a, b)=>a - b).pop();
return result;
}
/**
* Verifies that attempting to coerce n to string or number won't throw a TypeError.
*/ function isNonPrimitive(n) {
return typeof n === 'symbol' || typeof n === 'object' && n !== null && !(Symbol.toPrimitive in n || 'toString' in n || 'valueOf' in n);
}
function isNumber(n) {
return !isNonPrimitive(n) && !isNaN(parseFloat(n)) && isFinite(n);
}
function almostWhole(x, epsilon) {
const rounded = Math.round(x);
return rounded - epsilon <= x && rounded + epsilon >= x;
}
/**
* @private
*/ function _setMinAndMaxByKey(array, target, property) {
let i, ilen, value;
for(i = 0, ilen = array.length; i < ilen; i++){
value = array[i][property];
if (!isNaN(value)) {
target.min = Math.min(target.min, value);
target.max = Math.max(target.max, value);
}
}
}
function toRadians(degrees) {
return degrees * (PI / 180);
}
function toDegrees(radians) {
return radians * (180 / PI);
}
/**
* Returns the number of decimal places
* i.e. the number of digits after the decimal point, of the value of this Number.
* @param x - A number.
* @returns The number of decimal places.
* @private
*/ function _decimalPlaces(x) {
if (!isNumberFinite(x)) {
return;
}
let e = 1;
let p = 0;
while(Math.round(x * e) / e !== x){
e *= 10;
p++;
}
return p;
}
// Gets the angle from vertical upright to the point about a centre.
function getAngleFromPoint(centrePoint, anglePoint) {
const distanceFromXCenter = anglePoint.x - centrePoint.x;
const distanceFromYCenter = anglePoint.y - centrePoint.y;
const radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);
let angle = Math.atan2(distanceFromYCenter, distanceFromXCenter);
if (angle < -0.5 * PI) {
angle += TAU; // make sure the returned angle is in the range of (-PI/2, 3PI/2]
}
return {
angle,
distance: radialDistanceFromCenter
};
}
function distanceBetweenPoints(pt1, pt2) {
return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2));
}
/**
* Shortest distance between angles, in either direction.
* @private
*/ function _angleDiff(a, b) {
return (a - b + PITAU) % TAU - PI;
}
/**
* Normalize angle to be between 0 and 2*PI
* @private
*/ function _normalizeAngle(a) {
return (a % TAU + TAU) % TAU;
}
/**
* @private
*/ function _angleBetween(angle, start, end, sameAngleIsFullCircle) {
const a = _normalizeAngle(angle);
const s = _normalizeAngle(start);
const e = _normalizeAngle(end);
const angleToStart = _normalizeAngle(s - a);
const angleToEnd = _normalizeAngle(e - a);
const startToAngle = _normalizeAngle(a - s);
const endToAngle = _normalizeAngle(a - e);
return a === s || a === e || sameAngleIsFullCircle && s === e || angleToStart > angleToEnd && startToAngle < endToAngle;
}
/**
* Limit `value` between `min` and `max`
* @param value
* @param min
* @param max
* @private
*/ function _limitValue(value, min, max) {
return Math.max(min, Math.min(max, value));
}
/**
* @param {number} value
* @private
*/ function _int16Range(value) {
return _limitValue(value, -32768, 32767);
}
/**
* @param value
* @param start
* @param end
* @param [epsilon]
* @private
*/ function _isBetween(value, start, end, epsilon = 1e-6) {
return value >= Math.min(start, end) - epsilon && value <= Math.max(start, end) + epsilon;
}
function _lookup(table, value, cmp) {
cmp = cmp || ((index)=>table[index] < value);
let hi = table.length - 1;
let lo = 0;
let mid;
while(hi - lo > 1){
mid = lo + hi >> 1;
if (cmp(mid)) {
lo = mid;
} else {
hi = mid;
}
}
return {
lo,
hi
};
}
/**
* Binary search
* @param table - the table search. must be sorted!
* @param key - property name for the value in each entry
* @param value - value to find
* @param last - lookup last index
* @private
*/ const _lookupByKey = (table, key, value, last)=>_lookup(table, value, last ? (index)=>{
const ti = table[index][key];
return ti < value || ti === value && table[index + 1][key] === value;
} : (index)=>table[index][key] < value);
/**
* Reverse binary search
* @param table - the table search. must be sorted!
* @param key - property name for the value in each entry
* @param value - value to find
* @private
*/ const _rlookupByKey = (table, key, value)=>_lookup(table, value, (index)=>table[index][key] >= value);
/**
* Return subset of `values` between `min` and `max` inclusive.
* Values are assumed to be in sorted order.
* @param values - sorted array of values
* @param min - min value
* @param max - max value
*/ function _filterBetween(values, min, max) {
let start = 0;
let end = values.length;
while(start < end && values[start] < min){
start++;
}
while(end > start && values[end - 1] > max){
end--;
}
return start > 0 || end < values.length ? values.slice(start, end) : values;
}
const arrayEvents = [
'push',
'pop',
'shift',
'splice',
'unshift'
];
function listenArrayEvents(array, listener) {
if (array._chartjs) {
array._chartjs.listeners.push(listener);
return;
}
Object.defineProperty(array, '_chartjs', {
configurable: true,
enumerable: false,
value: {
listeners: [
listener
]
}
});
arrayEvents.forEach((key)=>{
const method = '_onData' + _capitalize(key);
const base = array[key];
Object.defineProperty(array, key, {
configurable: true,
enumerable: false,
value (...args) {
const res = base.apply(this, args);
array._chartjs.listeners.forEach((object)=>{
if (typeof object[method] === 'function') {
object[method](...args);
}
});
return res;
}
});
});
}
function unlistenArrayEvents(array, listener) {
const stub = array._chartjs;
if (!stub) {
return;
}
const listeners = stub.listeners;
const index = listeners.indexOf(listener);
if (index !== -1) {
listeners.splice(index, 1);
}
if (listeners.length > 0) {
return;
}
arrayEvents.forEach((key)=>{
delete array[key];
});
delete array._chartjs;
}
/**
* @param items
*/ function _arrayUnique(items) {
const set = new Set(items);
if (set.size === items.length) {
return items;
}
return Array.from(set);
}
function fontString(pixelSize, fontStyle, fontFamily) {
return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;
}
/**
* Request animation polyfill
*/ const requestAnimFrame = function() {
if (typeof window === 'undefined') {
return function(callback) {
return callback();
};
}
return window.requestAnimationFrame;
}();
/**
* Throttles calling `fn` once per animation frame
* Latest arguments are used on the actual call
*/ function throttled(fn, thisArg) {
let argsToUse = [];
let ticking = false;
return function(...args) {
// Save the args for use later
argsToUse = args;
if (!ticking) {
ticking = true;
requestAnimFrame.call(window, ()=>{
ticking = false;
fn.apply(thisArg, argsToUse);
});
}
};
}
/**
* Debounces calling `fn` for `delay` ms
*/ function debounce(fn, delay) {
let timeout;
return function(...args) {
if (delay) {
clearTimeout(timeout);
timeout = setTimeout(fn, delay, args);
} else {
fn.apply(this, args);
}
return delay;
};
}
/**
* Converts 'start' to 'left', 'end' to 'right' and others to 'center'
* @private
*/ const _toLeftRightCenter = (align)=>align === 'start' ? 'left' : align === 'end' ? 'right' : 'center';
/**
* Returns `start`, `end` or `(start + end) / 2` depending on `align`. Defaults to `center`
* @private
*/ const _alignStartEnd = (align, start, end)=>align === 'start' ? start : align === 'end' ? end : (start + end) / 2;
/**
* Returns `left`, `right` or `(left + right) / 2` depending on `align`. Defaults to `left`
* @private
*/ const _textX = (align, left, right, rtl)=>{
const check = rtl ? 'left' : 'right';
return align === check ? right : align === 'center' ? (left + right) / 2 : left;
};
/**
* Return start and count of visible points.
* @private
*/ function _getStartAndCountOfVisiblePoints(meta, points, animationsDisabled) {
const pointCount = points.length;
let start = 0;
let count = pointCount;
if (meta._sorted) {
const { iScale , vScale , _parsed } = meta;
const spanGaps = meta.dataset ? meta.dataset.options ? meta.dataset.options.spanGaps : null : null;
const axis = iScale.axis;
const { min , max , minDefined , maxDefined } = iScale.getUserBounds();
if (minDefined) {
start = Math.min(// @ts-expect-error Need to type _parsed
_lookupByKey(_parsed, axis, min).lo, // @ts-expect-error Need to fix types on _lookupByKey
animationsDisabled ? pointCount : _lookupByKey(points, axis, iScale.getPixelForValue(min)).lo);
if (spanGaps) {
const distanceToDefinedLo = _parsed.slice(0, start + 1).reverse().findIndex((point)=>!isNullOrUndef(point[vScale.axis]));
start -= Math.max(0, distanceToDefinedLo);
}
start = _limitValue(start, 0, pointCount - 1);
}
if (maxDefined) {
let end = Math.max(// @ts-expect-error Need to type _parsed
_lookupByKey(_parsed, iScale.axis, max, true).hi + 1, // @ts-expect-error Need to fix types on _lookupByKey
animationsDisabled ? 0 : _lookupByKey(points, axis, iScale.getPixelForValue(max), true).hi + 1);
if (spanGaps) {
const distanceToDefinedHi = _parsed.slice(end - 1).findIndex((point)=>!isNullOrUndef(point[vScale.axis]));
end += Math.max(0, distanceToDefinedHi);
}
count = _limitValue(end, start, pointCount) - start;
} else {
count = pointCount - start;
}
}
return {
start,
count
};
}
/**
* Checks if the scale ranges have changed.
* @param {object} meta - dataset meta.
* @returns {boolean}
* @private
*/ function _scaleRangesChanged(meta) {
const { xScale , yScale , _scaleRanges } = meta;
const newRanges = {
xmin: xScale.min,
xmax: xScale.max,
ymin: yScale.min,
ymax: yScale.max
};
if (!_scaleRanges) {
meta._scaleRanges = newRanges;
return true;
}
const changed = _scaleRanges.xmin !== xScale.min || _scaleRanges.xmax !== xScale.max || _scaleRanges.ymin !== yScale.min || _scaleRanges.ymax !== yScale.max;
Object.assign(_scaleRanges, newRanges);
return changed;
}
const atEdge = (t)=>t === 0 || t === 1;
const elasticIn = (t, s, p)=>-(Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * TAU / p));
const elasticOut = (t, s, p)=>Math.pow(2, -10 * t) * Math.sin((t - s) * TAU / p) + 1;
/**
* Easing functions adapted from Robert Penner's easing equations.
* @namespace Chart.helpers.easing.effects
* @see http://www.robertpenner.com/easing/
*/ const effects = {
linear: (t)=>t,
easeInQuad: (t)=>t * t,
easeOutQuad: (t)=>-t * (t - 2),
easeInOutQuad: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t : -0.5 * (--t * (t - 2) - 1),
easeInCubic: (t)=>t * t * t,
easeOutCubic: (t)=>(t -= 1) * t * t + 1,
easeInOutCubic: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t * t : 0.5 * ((t -= 2) * t * t + 2),
easeInQuart: (t)=>t * t * t * t,
easeOutQuart: (t)=>-((t -= 1) * t * t * t - 1),
easeInOutQuart: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t * t * t : -0.5 * ((t -= 2) * t * t * t - 2),
easeInQuint: (t)=>t * t * t * t * t,
easeOutQuint: (t)=>(t -= 1) * t * t * t * t + 1,
easeInOutQuint: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t * t * t * t : 0.5 * ((t -= 2) * t * t * t * t + 2),
easeInSine: (t)=>-Math.cos(t * HALF_PI) + 1,
easeOutSine: (t)=>Math.sin(t * HALF_PI),
easeInOutSine: (t)=>-0.5 * (Math.cos(PI * t) - 1),
easeInExpo: (t)=>t === 0 ? 0 : Math.pow(2, 10 * (t - 1)),
easeOutExpo: (t)=>t === 1 ? 1 : -Math.pow(2, -10 * t) + 1,
easeInOutExpo: (t)=>atEdge(t) ? t : t < 0.5 ? 0.5 * Math.pow(2, 10 * (t * 2 - 1)) : 0.5 * (-Math.pow(2, -10 * (t * 2 - 1)) + 2),
easeInCirc: (t)=>t >= 1 ? t : -(Math.sqrt(1 - t * t) - 1),
easeOutCirc: (t)=>Math.sqrt(1 - (t -= 1) * t),
easeInOutCirc: (t)=>(t /= 0.5) < 1 ? -0.5 * (Math.sqrt(1 - t * t) - 1) : 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1),
easeInElastic: (t)=>atEdge(t) ? t : elasticIn(t, 0.075, 0.3),
easeOutElastic: (t)=>atEdge(t) ? t : elasticOut(t, 0.075, 0.3),
easeInOutElastic (t) {
const s = 0.1125;
const p = 0.45;
return atEdge(t) ? t : t < 0.5 ? 0.5 * elasticIn(t * 2, s, p) : 0.5 + 0.5 * elasticOut(t * 2 - 1, s, p);
},
easeInBack (t) {
const s = 1.70158;
return t * t * ((s + 1) * t - s);
},
easeOutBack (t) {
const s = 1.70158;
return (t -= 1) * t * ((s + 1) * t + s) + 1;
},
easeInOutBack (t) {
let s = 1.70158;
if ((t /= 0.5) < 1) {
return 0.5 * (t * t * (((s *= 1.525) + 1) * t - s));
}
return 0.5 * ((t -= 2) * t * (((s *= 1.525) + 1) * t + s) + 2);
},
easeInBounce: (t)=>1 - effects.easeOutBounce(1 - t),
easeOutBounce (t) {
const m = 7.5625;
const d = 2.75;
if (t < 1 / d) {
return m * t * t;
}
if (t < 2 / d) {
return m * (t -= 1.5 / d) * t + 0.75;
}
if (t < 2.5 / d) {
return m * (t -= 2.25 / d) * t + 0.9375;
}
return m * (t -= 2.625 / d) * t + 0.984375;
},
easeInOutBounce: (t)=>t < 0.5 ? effects.easeInBounce(t * 2) * 0.5 : effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5
};
function isPatternOrGradient(value) {
if (value && typeof value === 'object') {
const type = value.toString();
return type === '[object CanvasPattern]' || type === '[object CanvasGradient]';
}
return false;
}
function color(value) {
return isPatternOrGradient(value) ? value : new color$1.Color(value);
}
function getHoverColor(value) {
return isPatternOrGradient(value) ? value : new color$1.Color(value).saturate(0.5).darken(0.1).hexString();
}
const numbers = [
'x',
'y',
'borderWidth',
'radius',
'tension'
];
const colors = [
'color',
'borderColor',
'backgroundColor'
];
function applyAnimationsDefaults(defaults) {
defaults.set('animation', {
delay: undefined,
duration: 1000,
easing: 'easeOutQuart',
fn: undefined,
from: undefined,
loop: undefined,
to: undefined,
type: undefined
});
defaults.describe('animation', {
_fallback: false,
_indexable: false,
_scriptable: (name)=>name !== 'onProgress' && name !== 'onComplete' && name !== 'fn'
});
defaults.set('animations', {
colors: {
type: 'color',
properties: colors
},
numbers: {
type: 'number',
properties: numbers
}
});
defaults.describe('animations', {
_fallback: 'animation'
});
defaults.set('transitions', {
active: {
animation: {
duration: 400
}
},
resize: {
animation: {
duration: 0
}
},
show: {
animations: {
colors: {
from: 'transparent'
},
visible: {
type: 'boolean',
duration: 0
}
}
},
hide: {
animations: {
colors: {
to: 'transparent'
},
visible: {
type: 'boolean',
easing: 'linear',
fn: (v)=>v | 0
}
}
}
});
}
function applyLayoutsDefaults(defaults) {
defaults.set('layout', {
autoPadding: true,
padding: {
top: 0,
right: 0,
bottom: 0,
left: 0
}
});
}
const intlCache = new Map();
function getNumberFormat(locale, options) {
options = options || {};
const cacheKey = locale + JSON.stringify(options);
let formatter = intlCache.get(cacheKey);
if (!formatter) {
formatter = new Intl.NumberFormat(locale, options);
intlCache.set(cacheKey, formatter);
}
return formatter;
}
function formatNumber(num, locale, options) {
return getNumberFormat(locale, options).format(num);
}
const formatters = {
values (value) {
return isArray(value) ? value : '' + value;
},
numeric (tickValue, index, ticks) {
if (tickValue === 0) {
return '0';
}
const locale = this.chart.options.locale;
let notation;
let delta = tickValue;
if (ticks.length > 1) {
const maxTick = Math.max(Math.abs(ticks[0].value), Math.abs(ticks[ticks.length - 1].value));
if (maxTick < 1e-4 || maxTick > 1e+15) {
notation = 'scientific';
}
delta = calculateDelta(tickValue, ticks);
}
const logDelta = log10(Math.abs(delta));
const numDecimal = isNaN(logDelta) ? 1 : Math.max(Math.min(-1 * Math.floor(logDelta), 20), 0);
const options = {
notation,
minimumFractionDigits: numDecimal,
maximumFractionDigits: numDecimal
};
Object.assign(options, this.options.ticks.format);
return formatNumber(tickValue, locale, options);
},
logarithmic (tickValue, index, ticks) {
if (tickValue === 0) {
return '0';
}
const remain = ticks[index].significand || tickValue / Math.pow(10, Math.floor(log10(tickValue)));
if ([
1,
2,
3,
5,
10,
15
].includes(remain) || index > 0.8 * ticks.length) {
return formatters.numeric.call(this, tickValue, index, ticks);
}
return '';
}
};
function calculateDelta(tickValue, ticks) {
let delta = ticks.length > 3 ? ticks[2].value - ticks[1].value : ticks[1].value - ticks[0].value;
if (Math.abs(delta) >= 1 && tickValue !== Math.floor(tickValue)) {
delta = tickValue - Math.floor(tickValue);
}
return delta;
}
var Ticks = {
formatters
};
function applyScaleDefaults(defaults) {
defaults.set('scale', {
display: true,
offset: false,
reverse: false,
beginAtZero: false,
bounds: 'ticks',
clip: true,
grace: 0,
grid: {
display: true,
lineWidth: 1,
drawOnChartArea: true,
drawTicks: true,
tickLength: 8,
tickWidth: (_ctx, options)=>options.lineWidth,
tickColor: (_ctx, options)=>options.color,
offset: false
},
border: {
display: true,
dash: [],
dashOffset: 0.0,
width: 1
},
title: {
display: false,
text: '',
padding: {
top: 4,
bottom: 4
}
},
ticks: {
minRotation: 0,
maxRotation: 50,
mirror: false,
textStrokeWidth: 0,
textStrokeColor: '',
padding: 3,
display: true,
autoSkip: true,
autoSkipPadding: 3,
labelOffset: 0,
callback: Ticks.formatters.values,
minor: {},
major: {},
align: 'center',
crossAlign: 'near',
showLabelBackdrop: false,
backdropColor: 'rgba(255, 255, 255, 0.75)',
backdropPadding: 2
}
});
defaults.route('scale.ticks', 'color', '', 'color');
defaults.route('scale.grid', 'color', '', 'borderColor');
defaults.route('scale.border', 'color', '', 'borderColor');
defaults.route('scale.title', 'color', '', 'color');
defaults.describe('scale', {
_fallback: false,
_scriptable: (name)=>!name.startsWith('before') && !name.startsWith('after') && name !== 'callback' && name !== 'parser',
_indexable: (name)=>name !== 'borderDash' && name !== 'tickBorderDash' && name !== 'dash'
});
defaults.describe('scales', {
_fallback: 'scale'
});
defaults.describe('scale.ticks', {
_scriptable: (name)=>name !== 'backdropPadding' && name !== 'callback',
_indexable: (name)=>name !== 'backdropPadding'
});
}
const overrides = Object.create(null);
const descriptors = Object.create(null);
function getScope$1(node, key) {
if (!key) {
return node;
}
const keys = key.split('.');
for(let i = 0, n = keys.length; i < n; ++i){
const k = keys[i];
node = node[k] || (node[k] = Object.create(null));
}
return node;
}
function set(root, scope, values) {
if (typeof scope === 'string') {
return merge(getScope$1(root, scope), values);
}
return merge(getScope$1(root, ''), scope);
}
class Defaults {
constructor(_descriptors, _appliers){
this.animation = undefined;
this.backgroundColor = 'rgba(0,0,0,0.1)';
this.borderColor = 'rgba(0,0,0,0.1)';
this.color = '#666';
this.datasets = {};
this.devicePixelRatio = (context)=>context.chart.platform.getDevicePixelRatio();
this.elements = {};
this.events = [
'mousemove',
'mouseout',
'click',
'touchstart',
'touchmove'
];
this.font = {
family: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
size: 12,
style: 'normal',
lineHeight: 1.2,
weight: null
};
this.hover = {};
this.hoverBackgroundColor = (ctx, options)=>getHoverColor(options.backgroundColor);
this.hoverBorderColor = (ctx, options)=>getHoverColor(options.borderColor);
this.hoverColor = (ctx, options)=>getHoverColor(options.color);
this.indexAxis = 'x';
this.interaction = {
mode: 'nearest',
intersect: true,
includeInvisible: false
};
this.maintainAspectRatio = true;
this.onHover = null;
this.onClick = null;
this.parsing = true;
this.plugins = {};
this.responsive = true;
this.scale = undefined;
this.scales = {};
this.showLine = true;
this.drawActiveElementsOnTop = true;
this.describe(_descriptors);
this.apply(_appliers);
}
set(scope, values) {
return set(this, scope, values);
}
get(scope) {
return getScope$1(this, scope);
}
describe(scope, values) {
return set(descriptors, scope, values);
}
override(scope, values) {
return set(overrides, scope, values);
}
route(scope, name, targetScope, targetName) {
const scopeObject = getScope$1(this, scope);
const targetScopeObject = getScope$1(this, targetScope);
const privateName = '_' + name;
Object.defineProperties(scopeObject, {
[privateName]: {
value: scopeObject[name],
writable: true
},
[name]: {
enumerable: true,
get () {
const local = this[privateName];
const target = targetScopeObject[targetName];
if (isObject(local)) {
return Object.assign({}, target, local);
}
return valueOrDefault(local, target);
},
set (value) {
this[privateName] = value;
}
}
});
}
apply(appliers) {
appliers.forEach((apply)=>apply(this));
}
}
var defaults = /* #__PURE__ */ new Defaults({
_scriptable: (name)=>!name.startsWith('on'),
_indexable: (name)=>name !== 'events',
hover: {
_fallback: 'interaction'
},
interaction: {
_scriptable: false,
_indexable: false
}
}, [
applyAnimationsDefaults,
applyLayoutsDefaults,
applyScaleDefaults
]);
/**
* Converts the given font object into a CSS font string.
* @param font - A font object.
* @return The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font
* @private
*/ function toFontString(font) {
if (!font || isNullOrUndef(font.size) || isNullOrUndef(font.family)) {
return null;
}
return (font.style ? font.style + ' ' : '') + (font.weight ? font.weight + ' ' : '') + font.size + 'px ' + font.family;
}
/**
* @private
*/ function _measureText(ctx, data, gc, longest, string) {
let textWidth = data[string];
if (!textWidth) {
textWidth = data[string] = ctx.measureText(string).width;
gc.push(string);
}
if (textWidth > longest) {
longest = textWidth;
}
return longest;
}
/**
* @private
*/ // eslint-disable-next-line complexity
function _longestText(ctx, font, arrayOfThings, cache) {
cache = cache || {};
let data = cache.data = cache.data || {};
let gc = cache.garbageCollect = cache.garbageCollect || [];
if (cache.font !== font) {
data = cache.data = {};
gc = cache.garbageCollect = [];
cache.font = font;
}
ctx.save();
ctx.font = font;
let longest = 0;
const ilen = arrayOfThings.length;
let i, j, jlen, thing, nestedThing;
for(i = 0; i < ilen; i++){
thing = arrayOfThings[i];
// Undefined strings and arrays should not be measured
if (thing !== undefined && thing !== null && !isArray(thing)) {
longest = _measureText(ctx, data, gc, longest, thing);
} else if (isArray(thing)) {
// if it is an array lets measure each element
// to do maybe simplify this function a bit so we can do this more recursively?
for(j = 0, jlen = thing.length; j < jlen; j++){
nestedThing = thing[j];
// Undefined strings and arrays should not be measured
if (nestedThing !== undefined && nestedThing !== null && !isArray(nestedThing)) {
longest = _measureText(ctx, data, gc, longest, nestedThing);
}
}
}
}
ctx.restore();
const gcLen = gc.length / 2;
if (gcLen > arrayOfThings.length) {
for(i = 0; i < gcLen; i++){
delete data[gc[i]];
}
gc.splice(0, gcLen);
}
return longest;
}
/**
* Returns the aligned pixel value to avoid anti-aliasing blur
* @param chart - The chart instance.
* @param pixel - A pixel value.
* @param width - The width of the element.
* @returns The aligned pixel value.
* @private
*/ function _alignPixel(chart, pixel, width) {
const devicePixelRatio = chart.currentDevicePixelRatio;
const halfWidth = width !== 0 ? Math.max(width / 2, 0.5) : 0;
return Math.round((pixel - halfWidth) * devicePixelRatio) / devicePixelRatio + halfWidth;
}
/**
* Clears the entire canvas.
*/ function clearCanvas(canvas, ctx) {
if (!ctx && !canvas) {
return;
}
ctx = ctx || canvas.getContext('2d');
ctx.save();
// canvas.width and canvas.height do not consider the canvas transform,
// while clearRect does
ctx.resetTransform();
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.restore();
}
function drawPoint(ctx, options, x, y) {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
drawPointLegend(ctx, options, x, y, null);
}
// eslint-disable-next-line complexity
function drawPointLegend(ctx, options, x, y, w) {
let type, xOffset, yOffset, size, cornerRadius, width, xOffsetW, yOffsetW;
const style = options.pointStyle;
const rotation = options.rotation;
const radius = options.radius;
let rad = (rotation || 0) * RAD_PER_DEG;
if (style && typeof style === 'object') {
type = style.toString();
if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {
ctx.save();
ctx.translate(x, y);
ctx.rotate(rad);
ctx.drawImage(style, -style.width / 2, -style.height / 2, style.width, style.height);
ctx.restore();
return;
}
}
if (isNaN(radius) || radius <= 0) {
return;
}
ctx.beginPath();
switch(style){
// Default includes circle
default:
if (w) {
ctx.ellipse(x, y, w / 2, radius, 0, 0, TAU);
} else {
ctx.arc(x, y, radius, 0, TAU);
}
ctx.closePath();
break;
case 'triangle':
width = w ? w / 2 : radius;
ctx.moveTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);
rad += TWO_THIRDS_PI;
ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);
rad += TWO_THIRDS_PI;
ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);
ctx.closePath();
break;
case 'rectRounded':
// NOTE: the rounded rect implementation changed to use `arc` instead of
// `quadraticCurveTo` since it generates better results when rect is
// almost a circle. 0.516 (instead of 0.5) produces results with visually
// closer proportion to the previous impl and it is inscribed in the
// circle with `radius`. For more details, see the following PRs:
// https://github.com/chartjs/Chart.js/issues/5597
// https://github.com/chartjs/Chart.js/issues/5858
cornerRadius = radius * 0.516;
size = radius - cornerRadius;
xOffset = Math.cos(rad + QUARTER_PI) * size;
xOffsetW = Math.cos(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size);
yOffset = Math.sin(rad + QUARTER_PI) * size;
yOffsetW = Math.sin(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size);
ctx.arc(x - xOffsetW, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI);
ctx.arc(x + yOffsetW, y - xOffset, cornerRadius, rad - HALF_PI, rad);
ctx.arc(x + xOffsetW, y + yOffset, cornerRadius, rad, rad + HALF_PI);
ctx.arc(x - yOffsetW, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI);
ctx.closePath();
break;
case 'rect':
if (!rotation) {
size = Math.SQRT1_2 * radius;
width = w ? w / 2 : size;
ctx.rect(x - width, y - size, 2 * width, 2 * size);
break;
}
rad += QUARTER_PI;
/* falls through */ case 'rectRot':
xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
xOffset = Math.cos(rad) * radius;
yOffset = Math.sin(rad) * radius;
yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
ctx.moveTo(x - xOffsetW, y - yOffset);
ctx.lineTo(x + yOffsetW, y - xOffset);
ctx.lineTo(x + xOffsetW, y + yOffset);
ctx.lineTo(x - yOffsetW, y + xOffset);
ctx.closePath();
break;
case 'crossRot':
rad += QUARTER_PI;
/* falls through */ case 'cross':
xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
xOffset = Math.cos(rad) * radius;
yOffset = Math.sin(rad) * radius;
yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
ctx.moveTo(x - xOffsetW, y - yOffset);
ctx.lineTo(x + xOffsetW, y + yOffset);
ctx.moveTo(x + yOffsetW, y - xOffset);
ctx.lineTo(x - yOffsetW, y + xOffset);
break;
case 'star':
xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
xOffset = Math.cos(rad) * radius;
yOffset = Math.sin(rad) * radius;
yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
ctx.moveTo(x - xOffsetW, y - yOffset);
ctx.lineTo(x + xOffsetW, y + yOffset);
ctx.moveTo(x + yOffsetW, y - xOffset);
ctx.lineTo(x - yOffsetW, y + xOffset);
rad += QUARTER_PI;
xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
xOffset = Math.cos(rad) * radius;
yOffset = Math.sin(rad) * radius;
yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
ctx.moveTo(x - xOffsetW, y - yOffset);
ctx.lineTo(x + xOffsetW, y + yOffset);
ctx.moveTo(x + yOffsetW, y - xOffset);
ctx.lineTo(x - yOffsetW, y + xOffset);
break;
case 'line':
xOffset = w ? w / 2 : Math.cos(rad) * radius;
yOffset = Math.sin(rad) * radius;
ctx.moveTo(x - xOffset, y - yOffset);
ctx.lineTo(x + xOffset, y + yOffset);
break;
case 'dash':
ctx.moveTo(x, y);
ctx.lineTo(x + Math.cos(rad) * (w ? w / 2 : radius), y + Math.sin(rad) * radius);
break;
case false:
ctx.closePath();
break;
}
ctx.fill();
if (options.borderWidth > 0) {
ctx.stroke();
}
}
/**
* Returns true if the point is inside the rectangle
* @param point - The point to test
* @param area - The rectangle
* @param margin - allowed margin
* @private
*/ function _isPointInArea(point, area, margin) {
margin = margin || 0.5; // margin - default is to match rounded decimals
return !area || point && point.x > area.left - margin && point.x < area.right + margin && point.y > area.top - margin && point.y < area.bottom + margin;
}
function clipArea(ctx, area) {
ctx.save();
ctx.beginPath();
ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);
ctx.clip();
}
function unclipArea(ctx) {
ctx.restore();
}
/**
* @private
*/ function _steppedLineTo(ctx, previous, target, flip, mode) {
if (!previous) {
return ctx.lineTo(target.x, target.y);
}
if (mode === 'middle') {
const midpoint = (previous.x + target.x) / 2.0;
ctx.lineTo(midpoint, previous.y);
ctx.lineTo(midpoint, target.y);
} else if (mode === 'after' !== !!flip) {
ctx.lineTo(previous.x, target.y);
} else {
ctx.lineTo(target.x, previous.y);
}
ctx.lineTo(target.x, target.y);
}
/**
* @private
*/ function _bezierCurveTo(ctx, previous, target, flip) {
if (!previous) {
return ctx.lineTo(target.x, target.y);
}
ctx.bezierCurveTo(flip ? previous.cp1x : previous.cp2x, flip ? previous.cp1y : previous.cp2y, flip ? target.cp2x : target.cp1x, flip ? target.cp2y : target.cp1y, target.x, target.y);
}
function setRenderOpts(ctx, opts) {
if (opts.translation) {
ctx.translate(opts.translation[0], opts.translation[1]);
}
if (!isNullOrUndef(opts.rotation)) {
ctx.rotate(opts.rotation);
}
if (opts.color) {
ctx.fillStyle = opts.color;
}
if (opts.textAlign) {
ctx.textAlign = opts.textAlign;
}
if (opts.textBaseline) {
ctx.textBaseline = opts.textBaseline;
}
}
function decorateText(ctx, x, y, line, opts) {
if (opts.strikethrough || opts.underline) {
/**
* Now that IE11 support has been dropped, we can use more
* of the TextMetrics object. The actual bounding boxes
* are unflagged in Chrome, Firefox, Edge, and Safari so they
* can be safely used.
* See https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics#Browser_compatibility
*/ const metrics = ctx.measureText(line);
const left = x - metrics.actualBoundingBoxLeft;
const right = x + metrics.actualBoundingBoxRight;
const top = y - metrics.actualBoundingBoxAscent;
const bottom = y + metrics.actualBoundingBoxDescent;
const yDecoration = opts.strikethrough ? (top + bottom) / 2 : bottom;
ctx.strokeStyle = ctx.fillStyle;
ctx.beginPath();
ctx.lineWidth = opts.decorationWidth || 2;
ctx.moveTo(left, yDecoration);
ctx.lineTo(right, yDecoration);
ctx.stroke();
}
}
function drawBackdrop(ctx, opts) {
const oldColor = ctx.fillStyle;
ctx.fillStyle = opts.color;
ctx.fillRect(opts.left, opts.top, opts.width, opts.height);
ctx.fillStyle = oldColor;
}
/**
* Render text onto the canvas
*/ function renderText(ctx, text, x, y, font, opts = {}) {
const lines = isArray(text) ? text : [
text
];
const stroke = opts.strokeWidth > 0 && opts.strokeColor !== '';
let i, line;
ctx.save();
ctx.font = font.string;
setRenderOpts(ctx, opts);
for(i = 0; i < lines.length; ++i){
line = lines[i];
if (opts.backdrop) {
drawBackdrop(ctx, opts.backdrop);
}
if (stroke) {
if (opts.strokeColor) {
ctx.strokeStyle = opts.strokeColor;
}
if (!isNullOrUndef(opts.strokeWidth)) {
ctx.lineWidth = opts.strokeWidth;
}
ctx.strokeText(line, x, y, opts.maxWidth);
}
ctx.fillText(line, x, y, opts.maxWidth);
decorateText(ctx, x, y, line, opts);
y += Number(font.lineHeight);
}
ctx.restore();
}
/**
* Add a path of a rectangle with rounded corners to the current sub-path
* @param ctx - Context
* @param rect - Bounding rect
*/ function addRoundedRectPath(ctx, rect) {
const { x , y , w , h , radius } = rect;
// top left arc
ctx.arc(x + radius.topLeft, y + radius.topLeft, radius.topLeft, 1.5 * PI, PI, true);
// line from top left to bottom left
ctx.lineTo(x, y + h - radius.bottomLeft);
// bottom left arc
ctx.arc(x + radius.bottomLeft, y + h - radius.bottomLeft, radius.bottomLeft, PI, HALF_PI, true);
// line from bottom left to bottom right
ctx.lineTo(x + w - radius.bottomRight, y + h);
// bottom right arc
ctx.arc(x + w - radius.bottomRight, y + h - radius.bottomRight, radius.bottomRight, HALF_PI, 0, true);
// line from bottom right to top right
ctx.lineTo(x + w, y + radius.topRight);
// top right arc
ctx.arc(x + w - radius.topRight, y + radius.topRight, radius.topRight, 0, -HALF_PI, true);
// line from top right to top left
ctx.lineTo(x + radius.topLeft, y);
}
const LINE_HEIGHT = /^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/;
const FONT_STYLE = /^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/;
/**
* @alias Chart.helpers.options
* @namespace
*/ /**
* Converts the given line height `value` in pixels for a specific font `size`.
* @param value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em').
* @param size - The font size (in pixels) used to resolve relative `value`.
* @returns The effective line height in pixels (size * 1.2 if value is invalid).
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
* @since 2.7.0
*/ function toLineHeight(value, size) {
const matches = ('' + value).match(LINE_HEIGHT);
if (!matches || matches[1] === 'normal') {
return size * 1.2;
}
value = +matches[2];
switch(matches[3]){
case 'px':
return value;
case '%':
value /= 100;
break;
}
return size * value;
}
const numberOrZero = (v)=>+v || 0;
function _readValueToProps(value, props) {
const ret = {};
const objProps = isObject(props);
const keys = objProps ? Object.keys(props) : props;
const read = isObject(value) ? objProps ? (prop)=>valueOrDefault(value[prop], value[props[prop]]) : (prop)=>value[prop] : ()=>value;
for (const prop of keys){
ret[prop] = numberOrZero(read(prop));
}
return ret;
}
/**
* Converts the given value into a TRBL object.
* @param value - If a number, set the value to all TRBL component,
* else, if an object, use defined properties and sets undefined ones to 0.
* x / y are shorthands for same value for left/right and top/bottom.
* @returns The padding values (top, right, bottom, left)
* @since 3.0.0
*/ function toTRBL(value) {
return _readValueToProps(value, {
top: 'y',
right: 'x',
bottom: 'y',
left: 'x'
});
}
/**
* Converts the given value into a TRBL corners object (similar with css border-radius).
* @param value - If a number, set the value to all TRBL corner components,
* else, if an object, use defined properties and sets undefined ones to 0.
* @returns The TRBL corner values (topLeft, topRight, bottomLeft, bottomRight)
* @since 3.0.0
*/ function toTRBLCorners(value) {
return _readValueToProps(value, [
'topLeft',
'topRight',
'bottomLeft',
'bottomRight'
]);
}
/**
* Converts the given value into a padding object with pre-computed width/height.
* @param value - If a number, set the value to all TRBL component,
* else, if an object, use defined properties and sets undefined ones to 0.
* x / y are shorthands for same value for left/right and top/bottom.
* @returns The padding values (top, right, bottom, left, width, height)
* @since 2.7.0
*/ function toPadding(value) {
const obj = toTRBL(value);
obj.width = obj.left + obj.right;
obj.height = obj.top + obj.bottom;
return obj;
}
/**
* Parses font options and returns the font object.
* @param options - A object that contains font options to be parsed.
* @param fallback - A object that contains fallback font options.
* @return The font object.
* @private
*/ function toFont(options, fallback) {
options = options || {};
fallback = fallback || defaults.font;
let size = valueOrDefault(options.size, fallback.size);
if (typeof size === 'string') {
size = parseInt(size, 10);
}
let style = valueOrDefault(options.style, fallback.style);
if (style && !('' + style).match(FONT_STYLE)) {
console.warn('Invalid font style specified: "' + style + '"');
style = undefined;
}
const font = {
family: valueOrDefault(options.family, fallback.family),
lineHeight: toLineHeight(valueOrDefault(options.lineHeight, fallback.lineHeight), size),
size,
style,
weight: valueOrDefault(options.weight, fallback.weight),
string: ''
};
font.string = toFontString(font);
return font;
}
/**
* Evaluates the given `inputs` sequentially and returns the first defined value.
* @param inputs - An array of values, falling back to the last value.
* @param context - If defined and the current value is a function, the value
* is called with `context` as first argument and the result becomes the new input.
* @param index - If defined and the current value is an array, the value
* at `index` become the new input.
* @param info - object to return information about resolution in
* @param info.cacheable - Will be set to `false` if option is not cacheable.
* @since 2.7.0
*/ function resolve(inputs, context, index, info) {
let cacheable = true;
let i, ilen, value;
for(i = 0, ilen = inputs.length; i < ilen; ++i){
value = inputs[i];
if (value === undefined) {
continue;
}
if (context !== undefined && typeof value === 'function') {
value = value(context);
cacheable = false;
}
if (index !== undefined && isArray(value)) {
value = value[index % value.length];
cacheable = false;
}
if (value !== undefined) {
if (info && !cacheable) {
info.cacheable = false;
}
return value;
}
}
}
/**
* @param minmax
* @param grace
* @param beginAtZero
* @private
*/ function _addGrace(minmax, grace, beginAtZero) {
const { min , max } = minmax;
const change = toDimension(grace, (max - min) / 2);
const keepZero = (value, add)=>beginAtZero && value === 0 ? 0 : value + add;
return {
min: keepZero(min, -Math.abs(change)),
max: keepZero(max, change)
};
}
function createContext(parentContext, context) {
return Object.assign(Object.create(parentContext), context);
}
/**
* Creates a Proxy for resolving raw values for options.
* @param scopes - The option scopes to look for values, in resolution order
* @param prefixes - The prefixes for values, in resolution order.
* @param rootScopes - The root option scopes
* @param fallback - Parent scopes fallback
* @param getTarget - callback for getting the target for changed values
* @returns Proxy
* @private
*/ function _createResolver(scopes, prefixes = [
''
], rootScopes, fallback, getTarget = ()=>scopes[0]) {
const finalRootScopes = rootScopes || scopes;
if (typeof fallback === 'undefined') {
fallback = _resolve('_fallback', scopes);
}
const cache = {
[Symbol.toStringTag]: 'Object',
_cacheable: true,
_scopes: scopes,
_rootScopes: finalRootScopes,
_fallback: fallback,
_getTarget: getTarget,
override: (scope)=>_createResolver([
scope,
...scopes
], prefixes, finalRootScopes, fallback)
};
return new Proxy(cache, {
/**
* A trap for the delete operator.
*/ deleteProperty (target, prop) {
delete target[prop]; // remove from cache
delete target._keys; // remove cached keys
delete scopes[0][prop]; // remove from top level scope
return true;
},
/**
* A trap for getting property values.
*/ get (target, prop) {
return _cached(target, prop, ()=>_resolveWithPrefixes(prop, prefixes, scopes, target));
},
/**
* A trap for Object.getOwnPropertyDescriptor.
* Also used by Object.hasOwnProperty.
*/ getOwnPropertyDescriptor (target, prop) {
return Reflect.getOwnPropertyDescriptor(target._scopes[0], prop);
},
/**
* A trap for Object.getPrototypeOf.
*/ getPrototypeOf () {
return Reflect.getPrototypeOf(scopes[0]);
},
/**
* A trap for the in operator.
*/ has (target, prop) {
return getKeysFromAllScopes(target).includes(prop);
},
/**
* A trap for Object.getOwnPropertyNames and Object.getOwnPropertySymbols.
*/ ownKeys (target) {
return getKeysFromAllScopes(target);
},
/**
* A trap for setting property values.
*/ set (target, prop, value) {
const storage = target._storage || (target._storage = getTarget());
target[prop] = storage[prop] = value; // set to top level scope + cache
delete target._keys; // remove cached keys
return true;
}
});
}
/**
* Returns an Proxy for resolving option values with context.
* @param proxy - The Proxy returned by `_createResolver`
* @param context - Context object for scriptable/indexable options
* @param subProxy - The proxy provided for scriptable options
* @param descriptorDefaults - Defaults for descriptors
* @private
*/ function _attachContext(proxy, context, subProxy, descriptorDefaults) {
const cache = {
_cacheable: false,
_proxy: proxy,
_context: context,
_subProxy: subProxy,
_stack: new Set(),
_descriptors: _descriptors(proxy, descriptorDefaults),
setContext: (ctx)=>_attachContext(proxy, ctx, subProxy, descriptorDefaults),
override: (scope)=>_attachContext(proxy.override(scope), context, subProxy, descriptorDefaults)
};
return new Proxy(cache, {
/**
* A trap for the delete operator.
*/ deleteProperty (target, prop) {
delete target[prop]; // remove from cache
delete proxy[prop]; // remove from proxy
return true;
},
/**
* A trap for getting property values.
*/ get (target, prop, receiver) {
return _cached(target, prop, ()=>_resolveWithContext(target, prop, receiver));
},
/**
* A trap for Object.getOwnPropertyDescriptor.
* Also used by Object.hasOwnProperty.
*/ getOwnPropertyDescriptor (target, prop) {
return target._descriptors.allKeys ? Reflect.has(proxy, prop) ? {
enumerable: true,
configurable: true
} : undefined : Reflect.getOwnPropertyDescriptor(proxy, prop);
},
/**
* A trap for Object.getPrototypeOf.
*/ getPrototypeOf () {
return Reflect.getPrototypeOf(proxy);
},
/**
* A trap for the in operator.
*/ has (target, prop) {
return Reflect.has(proxy, prop);
},
/**
* A trap for Object.getOwnPropertyNames and Object.getOwnPropertySymbols.
*/ ownKeys () {
return Reflect.ownKeys(proxy);
},
/**
* A trap for setting property values.
*/ set (target, prop, value) {
proxy[prop] = value; // set to proxy
delete target[prop]; // remove from cache
return true;
}
});
}
/**
* @private
*/ function _descriptors(proxy, defaults = {
scriptable: true,
indexable: true
}) {
const { _scriptable =defaults.scriptable , _indexable =defaults.indexable , _allKeys =defaults.allKeys } = proxy;
return {
allKeys: _allKeys,
scriptable: _scriptable,
indexable: _indexable,
isScriptable: isFunction(_scriptable) ? _scriptable : ()=>_scriptable,
isIndexable: isFunction(_indexable) ? _indexable : ()=>_indexable
};
}
const readKey = (prefix, name)=>prefix ? prefix + _capitalize(name) : name;
const needsSubResolver = (prop, value)=>isObject(value) && prop !== 'adapters' && (Object.getPrototypeOf(value) === null || value.constructor === Object);
function _cached(target, prop, resolve) {
if (Object.prototype.hasOwnProperty.call(target, prop) || prop === 'constructor') {
return target[prop];
}
const value = resolve();
// cache the resolved value
target[prop] = value;
return value;
}
function _resolveWithContext(target, prop, receiver) {
const { _proxy , _context , _subProxy , _descriptors: descriptors } = target;
let value = _proxy[prop]; // resolve from proxy
// resolve with context
if (isFunction(value) && descriptors.isScriptable(prop)) {
value = _resolveScriptable(prop, value, target, receiver);
}
if (isArray(value) && value.length) {
value = _resolveArray(prop, value, target, descriptors.isIndexable);
}
if (needsSubResolver(prop, value)) {
// if the resolved value is an object, create a sub resolver for it
value = _attachContext(value, _context, _subProxy && _subProxy[prop], descriptors);
}
return value;
}
function _resolveScriptable(prop, getValue, target, receiver) {
const { _proxy , _context , _subProxy , _stack } = target;
if (_stack.has(prop)) {
throw new Error('Recursion detected: ' + Array.from(_stack).join('->') + '->' + prop);
}
_stack.add(prop);
let value = getValue(_context, _subProxy || receiver);
_stack.delete(prop);
if (needsSubResolver(prop, value)) {
// When scriptable option returns an object, create a resolver on that.
value = createSubResolver(_proxy._scopes, _proxy, prop, value);
}
return value;
}
function _resolveArray(prop, value, target, isIndexable) {
const { _proxy , _context , _subProxy , _descriptors: descriptors } = target;
if (typeof _context.index !== 'undefined' && isIndexable(prop)) {
return value[_context.index % value.length];
} else if (isObject(value[0])) {
// Array of objects, return array or resolvers
const arr = value;
const scopes = _proxy._scopes.filter((s)=>s !== arr);
value = [];
for (const item of arr){
const resolver = createSubResolver(scopes, _proxy, prop, item);
value.push(_attachContext(resolver, _context, _subProxy && _subProxy[prop], descriptors));
}
}
return value;
}
function resolveFallback(fallback, prop, value) {
return isFunction(fallback) ? fallback(prop, value) : fallback;
}
const getScope = (key, parent)=>key === true ? parent : typeof key === 'string' ? resolveObjectKey(parent, key) : undefined;
function addScopes(set, parentScopes, key, parentFallback, value) {
for (const parent of parentScopes){
const scope = getScope(key, parent);
if (scope) {
set.add(scope);
const fallback = resolveFallback(scope._fallback, key, value);
if (typeof fallback !== 'undefined' && fallback !== key && fallback !== parentFallback) {
// When we reach the descriptor that defines a new _fallback, return that.
// The fallback will resume to that new scope.
return fallback;
}
} else if (scope === false && typeof parentFallback !== 'undefined' && key !== parentFallback) {
// Fallback to `false` results to `false`, when falling back to different key.
// For example `interaction` from `hover` or `plugins.tooltip` and `animation` from `animations`
return null;
}
}
return false;
}
function createSubResolver(parentScopes, resolver, prop, value) {
const rootScopes = resolver._rootScopes;
const fallback = resolveFallback(resolver._fallback, prop, value);
const allScopes = [
...parentScopes,
...rootScopes
];
const set = new Set();
set.add(value);
let key = addScopesFromKey(set, allScopes, prop, fallback || prop, value);
if (key === null) {
return false;
}
if (typeof fallback !== 'undefined' && fallback !== prop) {
key = addScopesFromKey(set, allScopes, fallback, key, value);
if (key === null) {
return false;
}
}
return _createResolver(Array.from(set), [
''
], rootScopes, fallback, ()=>subGetTarget(resolver, prop, value));
}
function addScopesFromKey(set, allScopes, key, fallback, item) {
while(key){
key = addScopes(set, allScopes, key, fallback, item);
}
return key;
}
function subGetTarget(resolver, prop, value) {
const parent = resolver._getTarget();
if (!(prop in parent)) {
parent[prop] = {};
}
const target = parent[prop];
if (isArray(target) && isObject(value)) {
// For array of objects, the object is used to store updated values
return value;
}
return target || {};
}
function _resolveWithPrefixes(prop, prefixes, scopes, proxy) {
let value;
for (const prefix of prefixes){
value = _resolve(readKey(prefix, prop), scopes);
if (typeof value !== 'undefined') {
return needsSubResolver(prop, value) ? createSubResolver(scopes, proxy, prop, value) : value;
}
}
}
function _resolve(key, scopes) {
for (const scope of scopes){
if (!scope) {
continue;
}
const value = scope[key];
if (typeof value !== 'undefined') {
return value;
}
}
}
function getKeysFromAllScopes(target) {
let keys = target._keys;
if (!keys) {
keys = target._keys = resolveKeysFromAllScopes(target._scopes);
}
return keys;
}
function resolveKeysFromAllScopes(scopes) {
const set = new Set();
for (const scope of scopes){
for (const key of Object.keys(scope).filter((k)=>!k.startsWith('_'))){
set.add(key);
}
}
return Array.from(set);
}
function _parseObjectDataRadialScale(meta, data, start, count) {
const { iScale } = meta;
const { key ='r' } = this._parsing;
const parsed = new Array(count);
let i, ilen, index, item;
for(i = 0, ilen = count; i < ilen; ++i){
index = i + start;
item = data[index];
parsed[i] = {
r: iScale.parse(resolveObjectKey(item, key), index)
};
}
return parsed;
}
const EPSILON = Number.EPSILON || 1e-14;
const getPoint = (points, i)=>i < points.length && !points[i].skip && points[i];
const getValueAxis = (indexAxis)=>indexAxis === 'x' ? 'y' : 'x';
function splineCurve(firstPoint, middlePoint, afterPoint, t) {
// Props to Rob Spencer at scaled innovation for his post on splining between points
// http://scaledinnovation.com/analytics/splines/aboutSplines.html
// This function must also respect "skipped" points
const previous = firstPoint.skip ? middlePoint : firstPoint;
const current = middlePoint;
const next = afterPoint.skip ? middlePoint : afterPoint;
const d01 = distanceBetweenPoints(current, previous);
const d12 = distanceBetweenPoints(next, current);
let s01 = d01 / (d01 + d12);
let s12 = d12 / (d01 + d12);
// If all points are the same, s01 & s02 will be inf
s01 = isNaN(s01) ? 0 : s01;
s12 = isNaN(s12) ? 0 : s12;
const fa = t * s01; // scaling factor for triangle Ta
const fb = t * s12;
return {
previous: {
x: current.x - fa * (next.x - previous.x),
y: current.y - fa * (next.y - previous.y)
},
next: {
x: current.x + fb * (next.x - previous.x),
y: current.y + fb * (next.y - previous.y)
}
};
}
/**
* Adjust tangents to ensure monotonic properties
*/ function monotoneAdjust(points, deltaK, mK) {
const pointsLen = points.length;
let alphaK, betaK, tauK, squaredMagnitude, pointCurrent;
let pointAfter = getPoint(points, 0);
for(let i = 0; i < pointsLen - 1; ++i){
pointCurrent = pointAfter;
pointAfter = getPoint(points, i + 1);
if (!pointCurrent || !pointAfter) {
continue;
}
if (almostEquals(deltaK[i], 0, EPSILON)) {
mK[i] = mK[i + 1] = 0;
continue;
}
alphaK = mK[i] / deltaK[i];
betaK = mK[i + 1] / deltaK[i];
squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2);
if (squaredMagnitude <= 9) {
continue;
}
tauK = 3 / Math.sqrt(squaredMagnitude);
mK[i] = alphaK * tauK * deltaK[i];
mK[i + 1] = betaK * tauK * deltaK[i];
}
}
function monotoneCompute(points, mK, indexAxis = 'x') {
const valueAxis = getValueAxis(indexAxis);
const pointsLen = points.length;
let delta, pointBefore, pointCurrent;
let pointAfter = getPoint(points, 0);
for(let i = 0; i < pointsLen; ++i){
pointBefore = pointCurrent;
pointCurrent = pointAfter;
pointAfter = getPoint(points, i + 1);
if (!pointCurrent) {
continue;
}
const iPixel = pointCurrent[indexAxis];
const vPixel = pointCurrent[valueAxis];
if (pointBefore) {
delta = (iPixel - pointBefore[indexAxis]) / 3;
pointCurrent[`cp1${indexAxis}`] = iPixel - delta;
pointCurrent[`cp1${valueAxis}`] = vPixel - delta * mK[i];
}
if (pointAfter) {
delta = (pointAfter[indexAxis] - iPixel) / 3;
pointCurrent[`cp2${indexAxis}`] = iPixel + delta;
pointCurrent[`cp2${valueAxis}`] = vPixel + delta * mK[i];
}
}
}
/**
* This function calculates Bézier control points in a similar way than |splineCurve|,
* but preserves monotonicity of the provided data and ensures no local extremums are added
* between the dataset discrete points due to the interpolation.
* See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation
*/ function splineCurveMonotone(points, indexAxis = 'x') {
const valueAxis = getValueAxis(indexAxis);
const pointsLen = points.length;
const deltaK = Array(pointsLen).fill(0);
const mK = Array(pointsLen);
// Calculate slopes (deltaK) and initialize tangents (mK)
let i, pointBefore, pointCurrent;
let pointAfter = getPoint(points, 0);
for(i = 0; i < pointsLen; ++i){
pointBefore = pointCurrent;
pointCurrent = pointAfter;
pointAfter = getPoint(points, i + 1);
if (!pointCurrent) {
continue;
}
if (pointAfter) {
const slopeDelta = pointAfter[indexAxis] - pointCurrent[indexAxis];
// In the case of two points that appear at the same x pixel, slopeDeltaX is 0
deltaK[i] = slopeDelta !== 0 ? (pointAfter[valueAxis] - pointCurrent[valueAxis]) / slopeDelta : 0;
}
mK[i] = !pointBefore ? deltaK[i] : !pointAfter ? deltaK[i - 1] : sign(deltaK[i - 1]) !== sign(deltaK[i]) ? 0 : (deltaK[i - 1] + deltaK[i]) / 2;
}
monotoneAdjust(points, deltaK, mK);
monotoneCompute(points, mK, indexAxis);
}
function capControlPoint(pt, min, max) {
return Math.max(Math.min(pt, max), min);
}
function capBezierPoints(points, area) {
let i, ilen, point, inArea, inAreaPrev;
let inAreaNext = _isPointInArea(points[0], area);
for(i = 0, ilen = points.length; i < ilen; ++i){
inAreaPrev = inArea;
inArea = inAreaNext;
inAreaNext = i < ilen - 1 && _isPointInArea(points[i + 1], area);
if (!inArea) {
continue;
}
point = points[i];
if (inAreaPrev) {
point.cp1x = capControlPoint(point.cp1x, area.left, area.right);
point.cp1y = capControlPoint(point.cp1y, area.top, area.bottom);
}
if (inAreaNext) {
point.cp2x = capControlPoint(point.cp2x, area.left, area.right);
point.cp2y = capControlPoint(point.cp2y, area.top, area.bottom);
}
}
}
/**
* @private
*/ function _updateBezierControlPoints(points, options, area, loop, indexAxis) {
let i, ilen, point, controlPoints;
// Only consider points that are drawn in case the spanGaps option is used
if (options.spanGaps) {
points = points.filter((pt)=>!pt.skip);
}
if (options.cubicInterpolationMode === 'monotone') {
splineCurveMonotone(points, indexAxis);
} else {
let prev = loop ? points[points.length - 1] : points[0];
for(i = 0, ilen = points.length; i < ilen; ++i){
point = points[i];
controlPoints = splineCurve(prev, point, points[Math.min(i + 1, ilen - (loop ? 0 : 1)) % ilen], options.tension);
point.cp1x = controlPoints.previous.x;
point.cp1y = controlPoints.previous.y;
point.cp2x = controlPoints.next.x;
point.cp2y = controlPoints.next.y;
prev = point;
}
}
if (options.capBezierPoints) {
capBezierPoints(points, area);
}
}
/**
* @private
*/ function _isDomSupported() {
return typeof window !== 'undefined' && typeof document !== 'undefined';
}
/**
* @private
*/ function _getParentNode(domNode) {
let parent = domNode.parentNode;
if (parent && parent.toString() === '[object ShadowRoot]') {
parent = parent.host;
}
return parent;
}
/**
* convert max-width/max-height values that may be percentages into a number
* @private
*/ function parseMaxStyle(styleValue, node, parentProperty) {
let valueInPixels;
if (typeof styleValue === 'string') {
valueInPixels = parseInt(styleValue, 10);
if (styleValue.indexOf('%') !== -1) {
// percentage * size in dimension
valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty];
}
} else {
valueInPixels = styleValue;
}
return valueInPixels;
}
const getComputedStyle = (element)=>element.ownerDocument.defaultView.getComputedStyle(element, null);
function getStyle(el, property) {
return getComputedStyle(el).getPropertyValue(property);
}
const positions = [
'top',
'right',
'bottom',
'left'
];
function getPositionedStyle(styles, style, suffix) {
const result = {};
suffix = suffix ? '-' + suffix : '';
for(let i = 0; i < 4; i++){
const pos = positions[i];
result[pos] = parseFloat(styles[style + '-' + pos + suffix]) || 0;
}
result.width = result.left + result.right;
result.height = result.top + result.bottom;
return result;
}
const useOffsetPos = (x, y, target)=>(x > 0 || y > 0) && (!target || !target.shadowRoot);
/**
* @param e
* @param canvas
* @returns Canvas position
*/ function getCanvasPosition(e, canvas) {
const touches = e.touches;
const source = touches && touches.length ? touches[0] : e;
const { offsetX , offsetY } = source;
let box = false;
let x, y;
if (useOffsetPos(offsetX, offsetY, e.target)) {
x = offsetX;
y = offsetY;
} else {
const rect = canvas.getBoundingClientRect();
x = source.clientX - rect.left;
y = source.clientY - rect.top;
box = true;
}
return {
x,
y,
box
};
}
/**
* Gets an event's x, y coordinates, relative to the chart area
* @param event
* @param chart
* @returns x and y coordinates of the event
*/ function getRelativePosition(event, chart) {
if ('native' in event) {
return event;
}
const { canvas , currentDevicePixelRatio } = chart;
const style = getComputedStyle(canvas);
const borderBox = style.boxSizing === 'border-box';
const paddings = getPositionedStyle(style, 'padding');
const borders = getPositionedStyle(style, 'border', 'width');
const { x , y , box } = getCanvasPosition(event, canvas);
const xOffset = paddings.left + (box && borders.left);
const yOffset = paddings.top + (box && borders.top);
let { width , height } = chart;
if (borderBox) {
width -= paddings.width + borders.width;
height -= paddings.height + borders.height;
}
return {
x: Math.round((x - xOffset) / width * canvas.width / currentDevicePixelRatio),
y: Math.round((y - yOffset) / height * canvas.height / currentDevicePixelRatio)
};
}
function getContainerSize(canvas, width, height) {
let maxWidth, maxHeight;
if (width === undefined || height === undefined) {
const container = canvas && _getParentNode(canvas);
if (!container) {
width = canvas.clientWidth;
height = canvas.clientHeight;
} else {
const rect = container.getBoundingClientRect(); // this is the border box of the container
const containerStyle = getComputedStyle(container);
const containerBorder = getPositionedStyle(containerStyle, 'border', 'width');
const containerPadding = getPositionedStyle(containerStyle, 'padding');
width = rect.width - containerPadding.width - containerBorder.width;
height = rect.height - containerPadding.height - containerBorder.height;
maxWidth = parseMaxStyle(containerStyle.maxWidth, container, 'clientWidth');
maxHeight = parseMaxStyle(containerStyle.maxHeight, container, 'clientHeight');
}
}
return {
width,
height,
maxWidth: maxWidth || INFINITY,
maxHeight: maxHeight || INFINITY
};
}
const round1 = (v)=>Math.round(v * 10) / 10;
// eslint-disable-next-line complexity
function getMaximumSize(canvas, bbWidth, bbHeight, aspectRatio) {
const style = getComputedStyle(canvas);
const margins = getPositionedStyle(style, 'margin');
const maxWidth = parseMaxStyle(style.maxWidth, canvas, 'clientWidth') || INFINITY;
const maxHeight = parseMaxStyle(style.maxHeight, canvas, 'clientHeight') || INFINITY;
const containerSize = getContainerSize(canvas, bbWidth, bbHeight);
let { width , height } = containerSize;
if (style.boxSizing === 'content-box') {
const borders = getPositionedStyle(style, 'border', 'width');
const paddings = getPositionedStyle(style, 'padding');
width -= paddings.width + borders.width;
height -= paddings.height + borders.height;
}
width = Math.max(0, width - margins.width);
height = Math.max(0, aspectRatio ? width / aspectRatio : height - margins.height);
width = round1(Math.min(width, maxWidth, containerSize.maxWidth));
height = round1(Math.min(height, maxHeight, containerSize.maxHeight));
if (width && !height) {
// https://github.com/chartjs/Chart.js/issues/4659
// If the canvas has width, but no height, default to aspectRatio of 2 (canvas default)
height = round1(width / 2);
}
const maintainHeight = bbWidth !== undefined || bbHeight !== undefined;
if (maintainHeight && aspectRatio && containerSize.height && height > containerSize.height) {
height = containerSize.height;
width = round1(Math.floor(height * aspectRatio));
}
return {
width,
height
};
}
/**
* @param chart
* @param forceRatio
* @param forceStyle
* @returns True if the canvas context size or transformation has changed.
*/ function retinaScale(chart, forceRatio, forceStyle) {
const pixelRatio = forceRatio || 1;
const deviceHeight = round1(chart.height * pixelRatio);
const deviceWidth = round1(chart.width * pixelRatio);
chart.height = round1(chart.height);
chart.width = round1(chart.width);
const canvas = chart.canvas;
// If no style has been set on the canvas, the render size is used as display size,
// making the chart visually bigger, so let's enforce it to the "correct" values.
// See https://github.com/chartjs/Chart.js/issues/3575
if (canvas.style && (forceStyle || !canvas.style.height && !canvas.style.width)) {
canvas.style.height = `${chart.height}px`;
canvas.style.width = `${chart.width}px`;
}
if (chart.currentDevicePixelRatio !== pixelRatio || canvas.height !== deviceHeight || canvas.width !== deviceWidth) {
chart.currentDevicePixelRatio = pixelRatio;
canvas.height = deviceHeight;
canvas.width = deviceWidth;
chart.ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
return true;
}
return false;
}
/**
* Detects support for options object argument in addEventListener.
* https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
* @private
*/ const supportsEventListenerOptions = function() {
let passiveSupported = false;
try {
const options = {
get passive () {
passiveSupported = true;
return false;
}
};
if (_isDomSupported()) {
window.addEventListener('test', null, options);
window.removeEventListener('test', null, options);
}
} catch (e) {
// continue regardless of error
}
return passiveSupported;
}();
/**
* The "used" size is the final value of a dimension property after all calculations have
* been performed. This method uses the computed style of `element` but returns undefined
* if the computed style is not expressed in pixels. That can happen in some cases where
* `element` has a size relative to its parent and this last one is not yet displayed,
* for example because of `display: none` on a parent node.
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
* @returns Size in pixels or undefined if unknown.
*/ function readUsedSize(element, property) {
const value = getStyle(element, property);
const matches = value && value.match(/^(\d+)(\.\d+)?px$/);
return matches ? +matches[1] : undefined;
}
/**
* @private
*/ function _pointInLine(p1, p2, t, mode) {
return {
x: p1.x + t * (p2.x - p1.x),
y: p1.y + t * (p2.y - p1.y)
};
}
/**
* @private
*/ function _steppedInterpolation(p1, p2, t, mode) {
return {
x: p1.x + t * (p2.x - p1.x),
y: mode === 'middle' ? t < 0.5 ? p1.y : p2.y : mode === 'after' ? t < 1 ? p1.y : p2.y : t > 0 ? p2.y : p1.y
};
}
/**
* @private
*/ function _bezierInterpolation(p1, p2, t, mode) {
const cp1 = {
x: p1.cp2x,
y: p1.cp2y
};
const cp2 = {
x: p2.cp1x,
y: p2.cp1y
};
const a = _pointInLine(p1, cp1, t);
const b = _pointInLine(cp1, cp2, t);
const c = _pointInLine(cp2, p2, t);
const d = _pointInLine(a, b, t);
const e = _pointInLine(b, c, t);
return _pointInLine(d, e, t);
}
const getRightToLeftAdapter = function(rectX, width) {
return {
x (x) {
return rectX + rectX + width - x;
},
setWidth (w) {
width = w;
},
textAlign (align) {
if (align === 'center') {
return align;
}
return align === 'right' ? 'left' : 'right';
},
xPlus (x, value) {
return x - value;
},
leftForLtr (x, itemWidth) {
return x - itemWidth;
}
};
};
const getLeftToRightAdapter = function() {
return {
x (x) {
return x;
},
setWidth (w) {},
textAlign (align) {
return align;
},
xPlus (x, value) {
return x + value;
},
leftForLtr (x, _itemWidth) {
return x;
}
};
};
function getRtlAdapter(rtl, rectX, width) {
return rtl ? getRightToLeftAdapter(rectX, width) : getLeftToRightAdapter();
}
function overrideTextDirection(ctx, direction) {
let style, original;
if (direction === 'ltr' || direction === 'rtl') {
style = ctx.canvas.style;
original = [
style.getPropertyValue('direction'),
style.getPropertyPriority('direction')
];
style.setProperty('direction', direction, 'important');
ctx.prevTextDirection = original;
}
}
function restoreTextDirection(ctx, original) {
if (original !== undefined) {
delete ctx.prevTextDirection;
ctx.canvas.style.setProperty('direction', original[0], original[1]);
}
}
function propertyFn(property) {
if (property === 'angle') {
return {
between: _angleBetween,
compare: _angleDiff,
normalize: _normalizeAngle
};
}
return {
between: _isBetween,
compare: (a, b)=>a - b,
normalize: (x)=>x
};
}
function normalizeSegment({ start , end , count , loop , style }) {
return {
start: start % count,
end: end % count,
loop: loop && (end - start + 1) % count === 0,
style
};
}
function getSegment(segment, points, bounds) {
const { property , start: startBound , end: endBound } = bounds;
const { between , normalize } = propertyFn(property);
const count = points.length;
let { start , end , loop } = segment;
let i, ilen;
if (loop) {
start += count;
end += count;
for(i = 0, ilen = count; i < ilen; ++i){
if (!between(normalize(points[start % count][property]), startBound, endBound)) {
break;
}
start--;
end--;
}
start %= count;
end %= count;
}
if (end < start) {
end += count;
}
return {
start,
end,
loop,
style: segment.style
};
}
function _boundSegment(segment, points, bounds) {
if (!bounds) {
return [
segment
];
}
const { property , start: startBound , end: endBound } = bounds;
const count = points.length;
const { compare , between , normalize } = propertyFn(property);
const { start , end , loop , style } = getSegment(segment, points, bounds);
const result = [];
let inside = false;
let subStart = null;
let value, point, prevValue;
const startIsBefore = ()=>between(startBound, prevValue, value) && compare(startBound, prevValue) !== 0;
const endIsBefore = ()=>compare(endBound, value) === 0 || between(endBound, prevValue, value);
const shouldStart = ()=>inside || startIsBefore();
const shouldStop = ()=>!inside || endIsBefore();
for(let i = start, prev = start; i <= end; ++i){
point = points[i % count];
if (point.skip) {
continue;
}
value = normalize(point[property]);
if (value === prevValue) {
continue;
}
inside = between(value, startBound, endBound);
if (subStart === null && shouldStart()) {
subStart = compare(value, startBound) === 0 ? i : prev;
}
if (subStart !== null && shouldStop()) {
result.push(normalizeSegment({
start: subStart,
end: i,
loop,
count,
style
}));
subStart = null;
}
prev = i;
prevValue = value;
}
if (subStart !== null) {
result.push(normalizeSegment({
start: subStart,
end,
loop,
count,
style
}));
}
return result;
}
function _boundSegments(line, bounds) {
const result = [];
const segments = line.segments;
for(let i = 0; i < segments.length; i++){
const sub = _boundSegment(segments[i], line.points, bounds);
if (sub.length) {
result.push(...sub);
}
}
return result;
}
function findStartAndEnd(points, count, loop, spanGaps) {
let start = 0;
let end = count - 1;
if (loop && !spanGaps) {
while(start < count && !points[start].skip){
start++;
}
}
while(start < count && points[start].skip){
start++;
}
start %= count;
if (loop) {
end += start;
}
while(end > start && points[end % count].skip){
end--;
}
end %= count;
return {
start,
end
};
}
function solidSegments(points, start, max, loop) {
const count = points.length;
const result = [];
let last = start;
let prev = points[start];
let end;
for(end = start + 1; end <= max; ++end){
const cur = points[end % count];
if (cur.skip || cur.stop) {
if (!prev.skip) {
loop = false;
result.push({
start: start % count,
end: (end - 1) % count,
loop
});
start = last = cur.stop ? end : null;
}
} else {
last = end;
if (prev.skip) {
start = end;
}
}
prev = cur;
}
if (last !== null) {
result.push({
start: start % count,
end: last % count,
loop
});
}
return result;
}
function _computeSegments(line, segmentOptions) {
const points = line.points;
const spanGaps = line.options.spanGaps;
const count = points.length;
if (!count) {
return [];
}
const loop = !!line._loop;
const { start , end } = findStartAndEnd(points, count, loop, spanGaps);
if (spanGaps === true) {
return splitByStyles(line, [
{
start,
end,
loop
}
], points, segmentOptions);
}
const max = end < start ? end + count : end;
const completeLoop = !!line._fullLoop && start === 0 && end === count - 1;
return splitByStyles(line, solidSegments(points, start, max, completeLoop), points, segmentOptions);
}
function splitByStyles(line, segments, points, segmentOptions) {
if (!segmentOptions || !segmentOptions.setContext || !points) {
return segments;
}
return doSplitByStyles(line, segments, points, segmentOptions);
}
function doSplitByStyles(line, segments, points, segmentOptions) {
const chartContext = line._chart.getContext();
const baseStyle = readStyle(line.options);
const { _datasetIndex: datasetIndex , options: { spanGaps } } = line;
const count = points.length;
const result = [];
let prevStyle = baseStyle;
let start = segments[0].start;
let i = start;
function addStyle(s, e, l, st) {
const dir = spanGaps ? -1 : 1;
if (s === e) {
return;
}
s += count;
while(points[s % count].skip){
s -= dir;
}
while(points[e % count].skip){
e += dir;
}
if (s % count !== e % count) {
result.push({
start: s % count,
end: e % count,
loop: l,
style: st
});
prevStyle = st;
start = e % count;
}
}
for (const segment of segments){
start = spanGaps ? start : segment.start;
let prev = points[start % count];
let style;
for(i = start + 1; i <= segment.end; i++){
const pt = points[i % count];
style = readStyle(segmentOptions.setContext(createContext(chartContext, {
type: 'segment',
p0: prev,
p1: pt,
p0DataIndex: (i - 1) % count,
p1DataIndex: i % count,
datasetIndex
})));
if (styleChanged(style, prevStyle)) {
addStyle(start, i - 1, segment.loop, prevStyle);
}
prev = pt;
prevStyle = style;
}
if (start < i - 1) {
addStyle(start, i - 1, segment.loop, prevStyle);
}
}
return result;
}
function readStyle(options) {
return {
backgroundColor: options.backgroundColor,
borderCapStyle: options.borderCapStyle,
borderDash: options.borderDash,
borderDashOffset: options.borderDashOffset,
borderJoinStyle: options.borderJoinStyle,
borderWidth: options.borderWidth,
borderColor: options.borderColor
};
}
function styleChanged(style, prevStyle) {
if (!prevStyle) {
return false;
}
const cache = [];
const replacer = function(key, value) {
if (!isPatternOrGradient(value)) {
return value;
}
if (!cache.includes(value)) {
cache.push(value);
}
return cache.indexOf(value);
};
return JSON.stringify(style, replacer) !== JSON.stringify(prevStyle, replacer);
}
function getSizeForArea(scale, chartArea, field) {
return scale.options.clip ? scale[field] : chartArea[field];
}
function getDatasetArea(meta, chartArea) {
const { xScale , yScale } = meta;
if (xScale && yScale) {
return {
left: getSizeForArea(xScale, chartArea, 'left'),
right: getSizeForArea(xScale, chartArea, 'right'),
top: getSizeForArea(yScale, chartArea, 'top'),
bottom: getSizeForArea(yScale, chartArea, 'bottom')
};
}
return chartArea;
}
function getDatasetClipArea(chart, meta) {
const clip = meta._clip;
if (clip.disabled) {
return false;
}
const area = getDatasetArea(meta, chart.chartArea);
return {
left: clip.left === false ? 0 : area.left - (clip.left === true ? 0 : clip.left),
right: clip.right === false ? chart.width : area.right + (clip.right === true ? 0 : clip.right),
top: clip.top === false ? 0 : area.top - (clip.top === true ? 0 : clip.top),
bottom: clip.bottom === false ? chart.height : area.bottom + (clip.bottom === true ? 0 : clip.bottom)
};
}
exports.HALF_PI = HALF_PI;
exports.INFINITY = INFINITY;
exports.PI = PI;
exports.PITAU = PITAU;
exports.QUARTER_PI = QUARTER_PI;
exports.RAD_PER_DEG = RAD_PER_DEG;
exports.TAU = TAU;
exports.TWO_THIRDS_PI = TWO_THIRDS_PI;
exports.Ticks = Ticks;
exports._addGrace = _addGrace;
exports._alignPixel = _alignPixel;
exports._alignStartEnd = _alignStartEnd;
exports._angleBetween = _angleBetween;
exports._angleDiff = _angleDiff;
exports._arrayUnique = _arrayUnique;
exports._attachContext = _attachContext;
exports._bezierCurveTo = _bezierCurveTo;
exports._bezierInterpolation = _bezierInterpolation;
exports._boundSegment = _boundSegment;
exports._boundSegments = _boundSegments;
exports._capitalize = _capitalize;
exports._computeSegments = _computeSegments;
exports._createResolver = _createResolver;
exports._decimalPlaces = _decimalPlaces;
exports._deprecated = _deprecated;
exports._descriptors = _descriptors;
exports._elementsEqual = _elementsEqual;
exports._factorize = _factorize;
exports._filterBetween = _filterBetween;
exports._getParentNode = _getParentNode;
exports._getStartAndCountOfVisiblePoints = _getStartAndCountOfVisiblePoints;
exports._int16Range = _int16Range;
exports._isBetween = _isBetween;
exports._isClickEvent = _isClickEvent;
exports._isDomSupported = _isDomSupported;
exports._isPointInArea = _isPointInArea;
exports._limitValue = _limitValue;
exports._longestText = _longestText;
exports._lookup = _lookup;
exports._lookupByKey = _lookupByKey;
exports._measureText = _measureText;
exports._merger = _merger;
exports._mergerIf = _mergerIf;
exports._normalizeAngle = _normalizeAngle;
exports._parseObjectDataRadialScale = _parseObjectDataRadialScale;
exports._pointInLine = _pointInLine;
exports._readValueToProps = _readValueToProps;
exports._rlookupByKey = _rlookupByKey;
exports._scaleRangesChanged = _scaleRangesChanged;
exports._setMinAndMaxByKey = _setMinAndMaxByKey;
exports._splitKey = _splitKey;
exports._steppedInterpolation = _steppedInterpolation;
exports._steppedLineTo = _steppedLineTo;
exports._textX = _textX;
exports._toLeftRightCenter = _toLeftRightCenter;
exports._updateBezierControlPoints = _updateBezierControlPoints;
exports.addRoundedRectPath = addRoundedRectPath;
exports.almostEquals = almostEquals;
exports.almostWhole = almostWhole;
exports.callback = callback;
exports.clearCanvas = clearCanvas;
exports.clipArea = clipArea;
exports.clone = clone;
exports.color = color;
exports.createContext = createContext;
exports.debounce = debounce;
exports.defaults = defaults;
exports.defined = defined;
exports.descriptors = descriptors;
exports.distanceBetweenPoints = distanceBetweenPoints;
exports.drawPoint = drawPoint;
exports.drawPointLegend = drawPointLegend;
exports.each = each;
exports.effects = effects;
exports.finiteOrDefault = finiteOrDefault;
exports.fontString = fontString;
exports.formatNumber = formatNumber;
exports.getAngleFromPoint = getAngleFromPoint;
exports.getDatasetClipArea = getDatasetClipArea;
exports.getHoverColor = getHoverColor;
exports.getMaximumSize = getMaximumSize;
exports.getRelativePosition = getRelativePosition;
exports.getRtlAdapter = getRtlAdapter;
exports.getStyle = getStyle;
exports.isArray = isArray;
exports.isFunction = isFunction;
exports.isNullOrUndef = isNullOrUndef;
exports.isNumber = isNumber;
exports.isNumberFinite = isNumberFinite;
exports.isObject = isObject;
exports.isPatternOrGradient = isPatternOrGradient;
exports.listenArrayEvents = listenArrayEvents;
exports.log10 = log10;
exports.merge = merge;
exports.mergeIf = mergeIf;
exports.niceNum = niceNum;
exports.noop = noop;
exports.overrideTextDirection = overrideTextDirection;
exports.overrides = overrides;
exports.readUsedSize = readUsedSize;
exports.renderText = renderText;
exports.requestAnimFrame = requestAnimFrame;
exports.resolve = resolve;
exports.resolveObjectKey = resolveObjectKey;
exports.restoreTextDirection = restoreTextDirection;
exports.retinaScale = retinaScale;
exports.setsEqual = setsEqual;
exports.sign = sign;
exports.splineCurve = splineCurve;
exports.splineCurveMonotone = splineCurveMonotone;
exports.supportsEventListenerOptions = supportsEventListenerOptions;
exports.throttled = throttled;
exports.toDegrees = toDegrees;
exports.toDimension = toDimension;
exports.toFont = toFont;
exports.toFontString = toFontString;
exports.toLineHeight = toLineHeight;
exports.toPadding = toPadding;
exports.toPercentage = toPercentage;
exports.toRadians = toRadians;
exports.toTRBL = toTRBL;
exports.toTRBLCorners = toTRBLCorners;
exports.uid = uid;
exports.unclipArea = unclipArea;
exports.unlistenArrayEvents = unlistenArrayEvents;
exports.valueOrDefault = valueOrDefault;
//# sourceMappingURL=helpers.dataset.cjs.map
File diff suppressed because one or more lines are too long
+2788
View File
@@ -0,0 +1,2788 @@
/*!
* Chart.js v4.5.1
* https://www.chartjs.org
* (c) 2025 Chart.js Contributors
* Released under the MIT License
*/
import { Color } from '@kurkle/color';
/**
* @namespace Chart.helpers
*/ /**
* An empty function that can be used, for example, for optional callback.
*/ function noop() {
/* noop */ }
/**
* Returns a unique id, sequentially generated from a global variable.
*/ const uid = (()=>{
let id = 0;
return ()=>id++;
})();
/**
* Returns true if `value` is neither null nor undefined, else returns false.
* @param value - The value to test.
* @since 2.7.0
*/ function isNullOrUndef(value) {
return value === null || value === undefined;
}
/**
* Returns true if `value` is an array (including typed arrays), else returns false.
* @param value - The value to test.
* @function
*/ function isArray(value) {
if (Array.isArray && Array.isArray(value)) {
return true;
}
const type = Object.prototype.toString.call(value);
if (type.slice(0, 7) === '[object' && type.slice(-6) === 'Array]') {
return true;
}
return false;
}
/**
* Returns true if `value` is an object (excluding null), else returns false.
* @param value - The value to test.
* @since 2.7.0
*/ function isObject(value) {
return value !== null && Object.prototype.toString.call(value) === '[object Object]';
}
/**
* Returns true if `value` is a finite number, else returns false
* @param value - The value to test.
*/ function isNumberFinite(value) {
return (typeof value === 'number' || value instanceof Number) && isFinite(+value);
}
/**
* Returns `value` if finite, else returns `defaultValue`.
* @param value - The value to return if defined.
* @param defaultValue - The value to return if `value` is not finite.
*/ function finiteOrDefault(value, defaultValue) {
return isNumberFinite(value) ? value : defaultValue;
}
/**
* Returns `value` if defined, else returns `defaultValue`.
* @param value - The value to return if defined.
* @param defaultValue - The value to return if `value` is undefined.
*/ function valueOrDefault(value, defaultValue) {
return typeof value === 'undefined' ? defaultValue : value;
}
const toPercentage = (value, dimension)=>typeof value === 'string' && value.endsWith('%') ? parseFloat(value) / 100 : +value / dimension;
const toDimension = (value, dimension)=>typeof value === 'string' && value.endsWith('%') ? parseFloat(value) / 100 * dimension : +value;
/**
* Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the
* value returned by `fn`. If `fn` is not a function, this method returns undefined.
* @param fn - The function to call.
* @param args - The arguments with which `fn` should be called.
* @param [thisArg] - The value of `this` provided for the call to `fn`.
*/ function callback(fn, args, thisArg) {
if (fn && typeof fn.call === 'function') {
return fn.apply(thisArg, args);
}
}
function each(loopable, fn, thisArg, reverse) {
let i, len, keys;
if (isArray(loopable)) {
len = loopable.length;
if (reverse) {
for(i = len - 1; i >= 0; i--){
fn.call(thisArg, loopable[i], i);
}
} else {
for(i = 0; i < len; i++){
fn.call(thisArg, loopable[i], i);
}
}
} else if (isObject(loopable)) {
keys = Object.keys(loopable);
len = keys.length;
for(i = 0; i < len; i++){
fn.call(thisArg, loopable[keys[i]], keys[i]);
}
}
}
/**
* Returns true if the `a0` and `a1` arrays have the same content, else returns false.
* @param a0 - The array to compare
* @param a1 - The array to compare
* @private
*/ function _elementsEqual(a0, a1) {
let i, ilen, v0, v1;
if (!a0 || !a1 || a0.length !== a1.length) {
return false;
}
for(i = 0, ilen = a0.length; i < ilen; ++i){
v0 = a0[i];
v1 = a1[i];
if (v0.datasetIndex !== v1.datasetIndex || v0.index !== v1.index) {
return false;
}
}
return true;
}
/**
* Returns a deep copy of `source` without keeping references on objects and arrays.
* @param source - The value to clone.
*/ function clone(source) {
if (isArray(source)) {
return source.map(clone);
}
if (isObject(source)) {
const target = Object.create(null);
const keys = Object.keys(source);
const klen = keys.length;
let k = 0;
for(; k < klen; ++k){
target[keys[k]] = clone(source[keys[k]]);
}
return target;
}
return source;
}
function isValidKey(key) {
return [
'__proto__',
'prototype',
'constructor'
].indexOf(key) === -1;
}
/**
* The default merger when Chart.helpers.merge is called without merger option.
* Note(SB): also used by mergeConfig and mergeScaleConfig as fallback.
* @private
*/ function _merger(key, target, source, options) {
if (!isValidKey(key)) {
return;
}
const tval = target[key];
const sval = source[key];
if (isObject(tval) && isObject(sval)) {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
merge(tval, sval, options);
} else {
target[key] = clone(sval);
}
}
function merge(target, source, options) {
const sources = isArray(source) ? source : [
source
];
const ilen = sources.length;
if (!isObject(target)) {
return target;
}
options = options || {};
const merger = options.merger || _merger;
let current;
for(let i = 0; i < ilen; ++i){
current = sources[i];
if (!isObject(current)) {
continue;
}
const keys = Object.keys(current);
for(let k = 0, klen = keys.length; k < klen; ++k){
merger(keys[k], target, current, options);
}
}
return target;
}
function mergeIf(target, source) {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
return merge(target, source, {
merger: _mergerIf
});
}
/**
* Merges source[key] in target[key] only if target[key] is undefined.
* @private
*/ function _mergerIf(key, target, source) {
if (!isValidKey(key)) {
return;
}
const tval = target[key];
const sval = source[key];
if (isObject(tval) && isObject(sval)) {
mergeIf(tval, sval);
} else if (!Object.prototype.hasOwnProperty.call(target, key)) {
target[key] = clone(sval);
}
}
/**
* @private
*/ function _deprecated(scope, value, previous, current) {
if (value !== undefined) {
console.warn(scope + ': "' + previous + '" is deprecated. Please use "' + current + '" instead');
}
}
// resolveObjectKey resolver cache
const keyResolvers = {
// Chart.helpers.core resolveObjectKey should resolve empty key to root object
'': (v)=>v,
// default resolvers
x: (o)=>o.x,
y: (o)=>o.y
};
/**
* @private
*/ function _splitKey(key) {
const parts = key.split('.');
const keys = [];
let tmp = '';
for (const part of parts){
tmp += part;
if (tmp.endsWith('\\')) {
tmp = tmp.slice(0, -1) + '.';
} else {
keys.push(tmp);
tmp = '';
}
}
return keys;
}
function _getKeyResolver(key) {
const keys = _splitKey(key);
return (obj)=>{
for (const k of keys){
if (k === '') {
break;
}
obj = obj && obj[k];
}
return obj;
};
}
function resolveObjectKey(obj, key) {
const resolver = keyResolvers[key] || (keyResolvers[key] = _getKeyResolver(key));
return resolver(obj);
}
/**
* @private
*/ function _capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
const defined = (value)=>typeof value !== 'undefined';
const isFunction = (value)=>typeof value === 'function';
// Adapted from https://stackoverflow.com/questions/31128855/comparing-ecma6-sets-for-equality#31129384
const setsEqual = (a, b)=>{
if (a.size !== b.size) {
return false;
}
for (const item of a){
if (!b.has(item)) {
return false;
}
}
return true;
};
/**
* @param e - The event
* @private
*/ function _isClickEvent(e) {
return e.type === 'mouseup' || e.type === 'click' || e.type === 'contextmenu';
}
/**
* @alias Chart.helpers.math
* @namespace
*/ const PI = Math.PI;
const TAU = 2 * PI;
const PITAU = TAU + PI;
const INFINITY = Number.POSITIVE_INFINITY;
const RAD_PER_DEG = PI / 180;
const HALF_PI = PI / 2;
const QUARTER_PI = PI / 4;
const TWO_THIRDS_PI = PI * 2 / 3;
const log10 = Math.log10;
const sign = Math.sign;
function almostEquals(x, y, epsilon) {
return Math.abs(x - y) < epsilon;
}
/**
* Implementation of the nice number algorithm used in determining where axis labels will go
*/ function niceNum(range) {
const roundedRange = Math.round(range);
range = almostEquals(range, roundedRange, range / 1000) ? roundedRange : range;
const niceRange = Math.pow(10, Math.floor(log10(range)));
const fraction = range / niceRange;
const niceFraction = fraction <= 1 ? 1 : fraction <= 2 ? 2 : fraction <= 5 ? 5 : 10;
return niceFraction * niceRange;
}
/**
* Returns an array of factors sorted from 1 to sqrt(value)
* @private
*/ function _factorize(value) {
const result = [];
const sqrt = Math.sqrt(value);
let i;
for(i = 1; i < sqrt; i++){
if (value % i === 0) {
result.push(i);
result.push(value / i);
}
}
if (sqrt === (sqrt | 0)) {
result.push(sqrt);
}
result.sort((a, b)=>a - b).pop();
return result;
}
/**
* Verifies that attempting to coerce n to string or number won't throw a TypeError.
*/ function isNonPrimitive(n) {
return typeof n === 'symbol' || typeof n === 'object' && n !== null && !(Symbol.toPrimitive in n || 'toString' in n || 'valueOf' in n);
}
function isNumber(n) {
return !isNonPrimitive(n) && !isNaN(parseFloat(n)) && isFinite(n);
}
function almostWhole(x, epsilon) {
const rounded = Math.round(x);
return rounded - epsilon <= x && rounded + epsilon >= x;
}
/**
* @private
*/ function _setMinAndMaxByKey(array, target, property) {
let i, ilen, value;
for(i = 0, ilen = array.length; i < ilen; i++){
value = array[i][property];
if (!isNaN(value)) {
target.min = Math.min(target.min, value);
target.max = Math.max(target.max, value);
}
}
}
function toRadians(degrees) {
return degrees * (PI / 180);
}
function toDegrees(radians) {
return radians * (180 / PI);
}
/**
* Returns the number of decimal places
* i.e. the number of digits after the decimal point, of the value of this Number.
* @param x - A number.
* @returns The number of decimal places.
* @private
*/ function _decimalPlaces(x) {
if (!isNumberFinite(x)) {
return;
}
let e = 1;
let p = 0;
while(Math.round(x * e) / e !== x){
e *= 10;
p++;
}
return p;
}
// Gets the angle from vertical upright to the point about a centre.
function getAngleFromPoint(centrePoint, anglePoint) {
const distanceFromXCenter = anglePoint.x - centrePoint.x;
const distanceFromYCenter = anglePoint.y - centrePoint.y;
const radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);
let angle = Math.atan2(distanceFromYCenter, distanceFromXCenter);
if (angle < -0.5 * PI) {
angle += TAU; // make sure the returned angle is in the range of (-PI/2, 3PI/2]
}
return {
angle,
distance: radialDistanceFromCenter
};
}
function distanceBetweenPoints(pt1, pt2) {
return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2));
}
/**
* Shortest distance between angles, in either direction.
* @private
*/ function _angleDiff(a, b) {
return (a - b + PITAU) % TAU - PI;
}
/**
* Normalize angle to be between 0 and 2*PI
* @private
*/ function _normalizeAngle(a) {
return (a % TAU + TAU) % TAU;
}
/**
* @private
*/ function _angleBetween(angle, start, end, sameAngleIsFullCircle) {
const a = _normalizeAngle(angle);
const s = _normalizeAngle(start);
const e = _normalizeAngle(end);
const angleToStart = _normalizeAngle(s - a);
const angleToEnd = _normalizeAngle(e - a);
const startToAngle = _normalizeAngle(a - s);
const endToAngle = _normalizeAngle(a - e);
return a === s || a === e || sameAngleIsFullCircle && s === e || angleToStart > angleToEnd && startToAngle < endToAngle;
}
/**
* Limit `value` between `min` and `max`
* @param value
* @param min
* @param max
* @private
*/ function _limitValue(value, min, max) {
return Math.max(min, Math.min(max, value));
}
/**
* @param {number} value
* @private
*/ function _int16Range(value) {
return _limitValue(value, -32768, 32767);
}
/**
* @param value
* @param start
* @param end
* @param [epsilon]
* @private
*/ function _isBetween(value, start, end, epsilon = 1e-6) {
return value >= Math.min(start, end) - epsilon && value <= Math.max(start, end) + epsilon;
}
function _lookup(table, value, cmp) {
cmp = cmp || ((index)=>table[index] < value);
let hi = table.length - 1;
let lo = 0;
let mid;
while(hi - lo > 1){
mid = lo + hi >> 1;
if (cmp(mid)) {
lo = mid;
} else {
hi = mid;
}
}
return {
lo,
hi
};
}
/**
* Binary search
* @param table - the table search. must be sorted!
* @param key - property name for the value in each entry
* @param value - value to find
* @param last - lookup last index
* @private
*/ const _lookupByKey = (table, key, value, last)=>_lookup(table, value, last ? (index)=>{
const ti = table[index][key];
return ti < value || ti === value && table[index + 1][key] === value;
} : (index)=>table[index][key] < value);
/**
* Reverse binary search
* @param table - the table search. must be sorted!
* @param key - property name for the value in each entry
* @param value - value to find
* @private
*/ const _rlookupByKey = (table, key, value)=>_lookup(table, value, (index)=>table[index][key] >= value);
/**
* Return subset of `values` between `min` and `max` inclusive.
* Values are assumed to be in sorted order.
* @param values - sorted array of values
* @param min - min value
* @param max - max value
*/ function _filterBetween(values, min, max) {
let start = 0;
let end = values.length;
while(start < end && values[start] < min){
start++;
}
while(end > start && values[end - 1] > max){
end--;
}
return start > 0 || end < values.length ? values.slice(start, end) : values;
}
const arrayEvents = [
'push',
'pop',
'shift',
'splice',
'unshift'
];
function listenArrayEvents(array, listener) {
if (array._chartjs) {
array._chartjs.listeners.push(listener);
return;
}
Object.defineProperty(array, '_chartjs', {
configurable: true,
enumerable: false,
value: {
listeners: [
listener
]
}
});
arrayEvents.forEach((key)=>{
const method = '_onData' + _capitalize(key);
const base = array[key];
Object.defineProperty(array, key, {
configurable: true,
enumerable: false,
value (...args) {
const res = base.apply(this, args);
array._chartjs.listeners.forEach((object)=>{
if (typeof object[method] === 'function') {
object[method](...args);
}
});
return res;
}
});
});
}
function unlistenArrayEvents(array, listener) {
const stub = array._chartjs;
if (!stub) {
return;
}
const listeners = stub.listeners;
const index = listeners.indexOf(listener);
if (index !== -1) {
listeners.splice(index, 1);
}
if (listeners.length > 0) {
return;
}
arrayEvents.forEach((key)=>{
delete array[key];
});
delete array._chartjs;
}
/**
* @param items
*/ function _arrayUnique(items) {
const set = new Set(items);
if (set.size === items.length) {
return items;
}
return Array.from(set);
}
function fontString(pixelSize, fontStyle, fontFamily) {
return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;
}
/**
* Request animation polyfill
*/ const requestAnimFrame = function() {
if (typeof window === 'undefined') {
return function(callback) {
return callback();
};
}
return window.requestAnimationFrame;
}();
/**
* Throttles calling `fn` once per animation frame
* Latest arguments are used on the actual call
*/ function throttled(fn, thisArg) {
let argsToUse = [];
let ticking = false;
return function(...args) {
// Save the args for use later
argsToUse = args;
if (!ticking) {
ticking = true;
requestAnimFrame.call(window, ()=>{
ticking = false;
fn.apply(thisArg, argsToUse);
});
}
};
}
/**
* Debounces calling `fn` for `delay` ms
*/ function debounce(fn, delay) {
let timeout;
return function(...args) {
if (delay) {
clearTimeout(timeout);
timeout = setTimeout(fn, delay, args);
} else {
fn.apply(this, args);
}
return delay;
};
}
/**
* Converts 'start' to 'left', 'end' to 'right' and others to 'center'
* @private
*/ const _toLeftRightCenter = (align)=>align === 'start' ? 'left' : align === 'end' ? 'right' : 'center';
/**
* Returns `start`, `end` or `(start + end) / 2` depending on `align`. Defaults to `center`
* @private
*/ const _alignStartEnd = (align, start, end)=>align === 'start' ? start : align === 'end' ? end : (start + end) / 2;
/**
* Returns `left`, `right` or `(left + right) / 2` depending on `align`. Defaults to `left`
* @private
*/ const _textX = (align, left, right, rtl)=>{
const check = rtl ? 'left' : 'right';
return align === check ? right : align === 'center' ? (left + right) / 2 : left;
};
/**
* Return start and count of visible points.
* @private
*/ function _getStartAndCountOfVisiblePoints(meta, points, animationsDisabled) {
const pointCount = points.length;
let start = 0;
let count = pointCount;
if (meta._sorted) {
const { iScale , vScale , _parsed } = meta;
const spanGaps = meta.dataset ? meta.dataset.options ? meta.dataset.options.spanGaps : null : null;
const axis = iScale.axis;
const { min , max , minDefined , maxDefined } = iScale.getUserBounds();
if (minDefined) {
start = Math.min(// @ts-expect-error Need to type _parsed
_lookupByKey(_parsed, axis, min).lo, // @ts-expect-error Need to fix types on _lookupByKey
animationsDisabled ? pointCount : _lookupByKey(points, axis, iScale.getPixelForValue(min)).lo);
if (spanGaps) {
const distanceToDefinedLo = _parsed.slice(0, start + 1).reverse().findIndex((point)=>!isNullOrUndef(point[vScale.axis]));
start -= Math.max(0, distanceToDefinedLo);
}
start = _limitValue(start, 0, pointCount - 1);
}
if (maxDefined) {
let end = Math.max(// @ts-expect-error Need to type _parsed
_lookupByKey(_parsed, iScale.axis, max, true).hi + 1, // @ts-expect-error Need to fix types on _lookupByKey
animationsDisabled ? 0 : _lookupByKey(points, axis, iScale.getPixelForValue(max), true).hi + 1);
if (spanGaps) {
const distanceToDefinedHi = _parsed.slice(end - 1).findIndex((point)=>!isNullOrUndef(point[vScale.axis]));
end += Math.max(0, distanceToDefinedHi);
}
count = _limitValue(end, start, pointCount) - start;
} else {
count = pointCount - start;
}
}
return {
start,
count
};
}
/**
* Checks if the scale ranges have changed.
* @param {object} meta - dataset meta.
* @returns {boolean}
* @private
*/ function _scaleRangesChanged(meta) {
const { xScale , yScale , _scaleRanges } = meta;
const newRanges = {
xmin: xScale.min,
xmax: xScale.max,
ymin: yScale.min,
ymax: yScale.max
};
if (!_scaleRanges) {
meta._scaleRanges = newRanges;
return true;
}
const changed = _scaleRanges.xmin !== xScale.min || _scaleRanges.xmax !== xScale.max || _scaleRanges.ymin !== yScale.min || _scaleRanges.ymax !== yScale.max;
Object.assign(_scaleRanges, newRanges);
return changed;
}
const atEdge = (t)=>t === 0 || t === 1;
const elasticIn = (t, s, p)=>-(Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * TAU / p));
const elasticOut = (t, s, p)=>Math.pow(2, -10 * t) * Math.sin((t - s) * TAU / p) + 1;
/**
* Easing functions adapted from Robert Penner's easing equations.
* @namespace Chart.helpers.easing.effects
* @see http://www.robertpenner.com/easing/
*/ const effects = {
linear: (t)=>t,
easeInQuad: (t)=>t * t,
easeOutQuad: (t)=>-t * (t - 2),
easeInOutQuad: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t : -0.5 * (--t * (t - 2) - 1),
easeInCubic: (t)=>t * t * t,
easeOutCubic: (t)=>(t -= 1) * t * t + 1,
easeInOutCubic: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t * t : 0.5 * ((t -= 2) * t * t + 2),
easeInQuart: (t)=>t * t * t * t,
easeOutQuart: (t)=>-((t -= 1) * t * t * t - 1),
easeInOutQuart: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t * t * t : -0.5 * ((t -= 2) * t * t * t - 2),
easeInQuint: (t)=>t * t * t * t * t,
easeOutQuint: (t)=>(t -= 1) * t * t * t * t + 1,
easeInOutQuint: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t * t * t * t : 0.5 * ((t -= 2) * t * t * t * t + 2),
easeInSine: (t)=>-Math.cos(t * HALF_PI) + 1,
easeOutSine: (t)=>Math.sin(t * HALF_PI),
easeInOutSine: (t)=>-0.5 * (Math.cos(PI * t) - 1),
easeInExpo: (t)=>t === 0 ? 0 : Math.pow(2, 10 * (t - 1)),
easeOutExpo: (t)=>t === 1 ? 1 : -Math.pow(2, -10 * t) + 1,
easeInOutExpo: (t)=>atEdge(t) ? t : t < 0.5 ? 0.5 * Math.pow(2, 10 * (t * 2 - 1)) : 0.5 * (-Math.pow(2, -10 * (t * 2 - 1)) + 2),
easeInCirc: (t)=>t >= 1 ? t : -(Math.sqrt(1 - t * t) - 1),
easeOutCirc: (t)=>Math.sqrt(1 - (t -= 1) * t),
easeInOutCirc: (t)=>(t /= 0.5) < 1 ? -0.5 * (Math.sqrt(1 - t * t) - 1) : 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1),
easeInElastic: (t)=>atEdge(t) ? t : elasticIn(t, 0.075, 0.3),
easeOutElastic: (t)=>atEdge(t) ? t : elasticOut(t, 0.075, 0.3),
easeInOutElastic (t) {
const s = 0.1125;
const p = 0.45;
return atEdge(t) ? t : t < 0.5 ? 0.5 * elasticIn(t * 2, s, p) : 0.5 + 0.5 * elasticOut(t * 2 - 1, s, p);
},
easeInBack (t) {
const s = 1.70158;
return t * t * ((s + 1) * t - s);
},
easeOutBack (t) {
const s = 1.70158;
return (t -= 1) * t * ((s + 1) * t + s) + 1;
},
easeInOutBack (t) {
let s = 1.70158;
if ((t /= 0.5) < 1) {
return 0.5 * (t * t * (((s *= 1.525) + 1) * t - s));
}
return 0.5 * ((t -= 2) * t * (((s *= 1.525) + 1) * t + s) + 2);
},
easeInBounce: (t)=>1 - effects.easeOutBounce(1 - t),
easeOutBounce (t) {
const m = 7.5625;
const d = 2.75;
if (t < 1 / d) {
return m * t * t;
}
if (t < 2 / d) {
return m * (t -= 1.5 / d) * t + 0.75;
}
if (t < 2.5 / d) {
return m * (t -= 2.25 / d) * t + 0.9375;
}
return m * (t -= 2.625 / d) * t + 0.984375;
},
easeInOutBounce: (t)=>t < 0.5 ? effects.easeInBounce(t * 2) * 0.5 : effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5
};
function isPatternOrGradient(value) {
if (value && typeof value === 'object') {
const type = value.toString();
return type === '[object CanvasPattern]' || type === '[object CanvasGradient]';
}
return false;
}
function color(value) {
return isPatternOrGradient(value) ? value : new Color(value);
}
function getHoverColor(value) {
return isPatternOrGradient(value) ? value : new Color(value).saturate(0.5).darken(0.1).hexString();
}
const numbers = [
'x',
'y',
'borderWidth',
'radius',
'tension'
];
const colors = [
'color',
'borderColor',
'backgroundColor'
];
function applyAnimationsDefaults(defaults) {
defaults.set('animation', {
delay: undefined,
duration: 1000,
easing: 'easeOutQuart',
fn: undefined,
from: undefined,
loop: undefined,
to: undefined,
type: undefined
});
defaults.describe('animation', {
_fallback: false,
_indexable: false,
_scriptable: (name)=>name !== 'onProgress' && name !== 'onComplete' && name !== 'fn'
});
defaults.set('animations', {
colors: {
type: 'color',
properties: colors
},
numbers: {
type: 'number',
properties: numbers
}
});
defaults.describe('animations', {
_fallback: 'animation'
});
defaults.set('transitions', {
active: {
animation: {
duration: 400
}
},
resize: {
animation: {
duration: 0
}
},
show: {
animations: {
colors: {
from: 'transparent'
},
visible: {
type: 'boolean',
duration: 0
}
}
},
hide: {
animations: {
colors: {
to: 'transparent'
},
visible: {
type: 'boolean',
easing: 'linear',
fn: (v)=>v | 0
}
}
}
});
}
function applyLayoutsDefaults(defaults) {
defaults.set('layout', {
autoPadding: true,
padding: {
top: 0,
right: 0,
bottom: 0,
left: 0
}
});
}
const intlCache = new Map();
function getNumberFormat(locale, options) {
options = options || {};
const cacheKey = locale + JSON.stringify(options);
let formatter = intlCache.get(cacheKey);
if (!formatter) {
formatter = new Intl.NumberFormat(locale, options);
intlCache.set(cacheKey, formatter);
}
return formatter;
}
function formatNumber(num, locale, options) {
return getNumberFormat(locale, options).format(num);
}
const formatters = {
values (value) {
return isArray(value) ? value : '' + value;
},
numeric (tickValue, index, ticks) {
if (tickValue === 0) {
return '0';
}
const locale = this.chart.options.locale;
let notation;
let delta = tickValue;
if (ticks.length > 1) {
const maxTick = Math.max(Math.abs(ticks[0].value), Math.abs(ticks[ticks.length - 1].value));
if (maxTick < 1e-4 || maxTick > 1e+15) {
notation = 'scientific';
}
delta = calculateDelta(tickValue, ticks);
}
const logDelta = log10(Math.abs(delta));
const numDecimal = isNaN(logDelta) ? 1 : Math.max(Math.min(-1 * Math.floor(logDelta), 20), 0);
const options = {
notation,
minimumFractionDigits: numDecimal,
maximumFractionDigits: numDecimal
};
Object.assign(options, this.options.ticks.format);
return formatNumber(tickValue, locale, options);
},
logarithmic (tickValue, index, ticks) {
if (tickValue === 0) {
return '0';
}
const remain = ticks[index].significand || tickValue / Math.pow(10, Math.floor(log10(tickValue)));
if ([
1,
2,
3,
5,
10,
15
].includes(remain) || index > 0.8 * ticks.length) {
return formatters.numeric.call(this, tickValue, index, ticks);
}
return '';
}
};
function calculateDelta(tickValue, ticks) {
let delta = ticks.length > 3 ? ticks[2].value - ticks[1].value : ticks[1].value - ticks[0].value;
if (Math.abs(delta) >= 1 && tickValue !== Math.floor(tickValue)) {
delta = tickValue - Math.floor(tickValue);
}
return delta;
}
var Ticks = {
formatters
};
function applyScaleDefaults(defaults) {
defaults.set('scale', {
display: true,
offset: false,
reverse: false,
beginAtZero: false,
bounds: 'ticks',
clip: true,
grace: 0,
grid: {
display: true,
lineWidth: 1,
drawOnChartArea: true,
drawTicks: true,
tickLength: 8,
tickWidth: (_ctx, options)=>options.lineWidth,
tickColor: (_ctx, options)=>options.color,
offset: false
},
border: {
display: true,
dash: [],
dashOffset: 0.0,
width: 1
},
title: {
display: false,
text: '',
padding: {
top: 4,
bottom: 4
}
},
ticks: {
minRotation: 0,
maxRotation: 50,
mirror: false,
textStrokeWidth: 0,
textStrokeColor: '',
padding: 3,
display: true,
autoSkip: true,
autoSkipPadding: 3,
labelOffset: 0,
callback: Ticks.formatters.values,
minor: {},
major: {},
align: 'center',
crossAlign: 'near',
showLabelBackdrop: false,
backdropColor: 'rgba(255, 255, 255, 0.75)',
backdropPadding: 2
}
});
defaults.route('scale.ticks', 'color', '', 'color');
defaults.route('scale.grid', 'color', '', 'borderColor');
defaults.route('scale.border', 'color', '', 'borderColor');
defaults.route('scale.title', 'color', '', 'color');
defaults.describe('scale', {
_fallback: false,
_scriptable: (name)=>!name.startsWith('before') && !name.startsWith('after') && name !== 'callback' && name !== 'parser',
_indexable: (name)=>name !== 'borderDash' && name !== 'tickBorderDash' && name !== 'dash'
});
defaults.describe('scales', {
_fallback: 'scale'
});
defaults.describe('scale.ticks', {
_scriptable: (name)=>name !== 'backdropPadding' && name !== 'callback',
_indexable: (name)=>name !== 'backdropPadding'
});
}
const overrides = Object.create(null);
const descriptors = Object.create(null);
function getScope$1(node, key) {
if (!key) {
return node;
}
const keys = key.split('.');
for(let i = 0, n = keys.length; i < n; ++i){
const k = keys[i];
node = node[k] || (node[k] = Object.create(null));
}
return node;
}
function set(root, scope, values) {
if (typeof scope === 'string') {
return merge(getScope$1(root, scope), values);
}
return merge(getScope$1(root, ''), scope);
}
class Defaults {
constructor(_descriptors, _appliers){
this.animation = undefined;
this.backgroundColor = 'rgba(0,0,0,0.1)';
this.borderColor = 'rgba(0,0,0,0.1)';
this.color = '#666';
this.datasets = {};
this.devicePixelRatio = (context)=>context.chart.platform.getDevicePixelRatio();
this.elements = {};
this.events = [
'mousemove',
'mouseout',
'click',
'touchstart',
'touchmove'
];
this.font = {
family: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
size: 12,
style: 'normal',
lineHeight: 1.2,
weight: null
};
this.hover = {};
this.hoverBackgroundColor = (ctx, options)=>getHoverColor(options.backgroundColor);
this.hoverBorderColor = (ctx, options)=>getHoverColor(options.borderColor);
this.hoverColor = (ctx, options)=>getHoverColor(options.color);
this.indexAxis = 'x';
this.interaction = {
mode: 'nearest',
intersect: true,
includeInvisible: false
};
this.maintainAspectRatio = true;
this.onHover = null;
this.onClick = null;
this.parsing = true;
this.plugins = {};
this.responsive = true;
this.scale = undefined;
this.scales = {};
this.showLine = true;
this.drawActiveElementsOnTop = true;
this.describe(_descriptors);
this.apply(_appliers);
}
set(scope, values) {
return set(this, scope, values);
}
get(scope) {
return getScope$1(this, scope);
}
describe(scope, values) {
return set(descriptors, scope, values);
}
override(scope, values) {
return set(overrides, scope, values);
}
route(scope, name, targetScope, targetName) {
const scopeObject = getScope$1(this, scope);
const targetScopeObject = getScope$1(this, targetScope);
const privateName = '_' + name;
Object.defineProperties(scopeObject, {
[privateName]: {
value: scopeObject[name],
writable: true
},
[name]: {
enumerable: true,
get () {
const local = this[privateName];
const target = targetScopeObject[targetName];
if (isObject(local)) {
return Object.assign({}, target, local);
}
return valueOrDefault(local, target);
},
set (value) {
this[privateName] = value;
}
}
});
}
apply(appliers) {
appliers.forEach((apply)=>apply(this));
}
}
var defaults = /* #__PURE__ */ new Defaults({
_scriptable: (name)=>!name.startsWith('on'),
_indexable: (name)=>name !== 'events',
hover: {
_fallback: 'interaction'
},
interaction: {
_scriptable: false,
_indexable: false
}
}, [
applyAnimationsDefaults,
applyLayoutsDefaults,
applyScaleDefaults
]);
/**
* Converts the given font object into a CSS font string.
* @param font - A font object.
* @return The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font
* @private
*/ function toFontString(font) {
if (!font || isNullOrUndef(font.size) || isNullOrUndef(font.family)) {
return null;
}
return (font.style ? font.style + ' ' : '') + (font.weight ? font.weight + ' ' : '') + font.size + 'px ' + font.family;
}
/**
* @private
*/ function _measureText(ctx, data, gc, longest, string) {
let textWidth = data[string];
if (!textWidth) {
textWidth = data[string] = ctx.measureText(string).width;
gc.push(string);
}
if (textWidth > longest) {
longest = textWidth;
}
return longest;
}
/**
* @private
*/ // eslint-disable-next-line complexity
function _longestText(ctx, font, arrayOfThings, cache) {
cache = cache || {};
let data = cache.data = cache.data || {};
let gc = cache.garbageCollect = cache.garbageCollect || [];
if (cache.font !== font) {
data = cache.data = {};
gc = cache.garbageCollect = [];
cache.font = font;
}
ctx.save();
ctx.font = font;
let longest = 0;
const ilen = arrayOfThings.length;
let i, j, jlen, thing, nestedThing;
for(i = 0; i < ilen; i++){
thing = arrayOfThings[i];
// Undefined strings and arrays should not be measured
if (thing !== undefined && thing !== null && !isArray(thing)) {
longest = _measureText(ctx, data, gc, longest, thing);
} else if (isArray(thing)) {
// if it is an array lets measure each element
// to do maybe simplify this function a bit so we can do this more recursively?
for(j = 0, jlen = thing.length; j < jlen; j++){
nestedThing = thing[j];
// Undefined strings and arrays should not be measured
if (nestedThing !== undefined && nestedThing !== null && !isArray(nestedThing)) {
longest = _measureText(ctx, data, gc, longest, nestedThing);
}
}
}
}
ctx.restore();
const gcLen = gc.length / 2;
if (gcLen > arrayOfThings.length) {
for(i = 0; i < gcLen; i++){
delete data[gc[i]];
}
gc.splice(0, gcLen);
}
return longest;
}
/**
* Returns the aligned pixel value to avoid anti-aliasing blur
* @param chart - The chart instance.
* @param pixel - A pixel value.
* @param width - The width of the element.
* @returns The aligned pixel value.
* @private
*/ function _alignPixel(chart, pixel, width) {
const devicePixelRatio = chart.currentDevicePixelRatio;
const halfWidth = width !== 0 ? Math.max(width / 2, 0.5) : 0;
return Math.round((pixel - halfWidth) * devicePixelRatio) / devicePixelRatio + halfWidth;
}
/**
* Clears the entire canvas.
*/ function clearCanvas(canvas, ctx) {
if (!ctx && !canvas) {
return;
}
ctx = ctx || canvas.getContext('2d');
ctx.save();
// canvas.width and canvas.height do not consider the canvas transform,
// while clearRect does
ctx.resetTransform();
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.restore();
}
function drawPoint(ctx, options, x, y) {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
drawPointLegend(ctx, options, x, y, null);
}
// eslint-disable-next-line complexity
function drawPointLegend(ctx, options, x, y, w) {
let type, xOffset, yOffset, size, cornerRadius, width, xOffsetW, yOffsetW;
const style = options.pointStyle;
const rotation = options.rotation;
const radius = options.radius;
let rad = (rotation || 0) * RAD_PER_DEG;
if (style && typeof style === 'object') {
type = style.toString();
if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {
ctx.save();
ctx.translate(x, y);
ctx.rotate(rad);
ctx.drawImage(style, -style.width / 2, -style.height / 2, style.width, style.height);
ctx.restore();
return;
}
}
if (isNaN(radius) || radius <= 0) {
return;
}
ctx.beginPath();
switch(style){
// Default includes circle
default:
if (w) {
ctx.ellipse(x, y, w / 2, radius, 0, 0, TAU);
} else {
ctx.arc(x, y, radius, 0, TAU);
}
ctx.closePath();
break;
case 'triangle':
width = w ? w / 2 : radius;
ctx.moveTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);
rad += TWO_THIRDS_PI;
ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);
rad += TWO_THIRDS_PI;
ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);
ctx.closePath();
break;
case 'rectRounded':
// NOTE: the rounded rect implementation changed to use `arc` instead of
// `quadraticCurveTo` since it generates better results when rect is
// almost a circle. 0.516 (instead of 0.5) produces results with visually
// closer proportion to the previous impl and it is inscribed in the
// circle with `radius`. For more details, see the following PRs:
// https://github.com/chartjs/Chart.js/issues/5597
// https://github.com/chartjs/Chart.js/issues/5858
cornerRadius = radius * 0.516;
size = radius - cornerRadius;
xOffset = Math.cos(rad + QUARTER_PI) * size;
xOffsetW = Math.cos(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size);
yOffset = Math.sin(rad + QUARTER_PI) * size;
yOffsetW = Math.sin(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size);
ctx.arc(x - xOffsetW, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI);
ctx.arc(x + yOffsetW, y - xOffset, cornerRadius, rad - HALF_PI, rad);
ctx.arc(x + xOffsetW, y + yOffset, cornerRadius, rad, rad + HALF_PI);
ctx.arc(x - yOffsetW, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI);
ctx.closePath();
break;
case 'rect':
if (!rotation) {
size = Math.SQRT1_2 * radius;
width = w ? w / 2 : size;
ctx.rect(x - width, y - size, 2 * width, 2 * size);
break;
}
rad += QUARTER_PI;
/* falls through */ case 'rectRot':
xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
xOffset = Math.cos(rad) * radius;
yOffset = Math.sin(rad) * radius;
yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
ctx.moveTo(x - xOffsetW, y - yOffset);
ctx.lineTo(x + yOffsetW, y - xOffset);
ctx.lineTo(x + xOffsetW, y + yOffset);
ctx.lineTo(x - yOffsetW, y + xOffset);
ctx.closePath();
break;
case 'crossRot':
rad += QUARTER_PI;
/* falls through */ case 'cross':
xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
xOffset = Math.cos(rad) * radius;
yOffset = Math.sin(rad) * radius;
yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
ctx.moveTo(x - xOffsetW, y - yOffset);
ctx.lineTo(x + xOffsetW, y + yOffset);
ctx.moveTo(x + yOffsetW, y - xOffset);
ctx.lineTo(x - yOffsetW, y + xOffset);
break;
case 'star':
xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
xOffset = Math.cos(rad) * radius;
yOffset = Math.sin(rad) * radius;
yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
ctx.moveTo(x - xOffsetW, y - yOffset);
ctx.lineTo(x + xOffsetW, y + yOffset);
ctx.moveTo(x + yOffsetW, y - xOffset);
ctx.lineTo(x - yOffsetW, y + xOffset);
rad += QUARTER_PI;
xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
xOffset = Math.cos(rad) * radius;
yOffset = Math.sin(rad) * radius;
yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
ctx.moveTo(x - xOffsetW, y - yOffset);
ctx.lineTo(x + xOffsetW, y + yOffset);
ctx.moveTo(x + yOffsetW, y - xOffset);
ctx.lineTo(x - yOffsetW, y + xOffset);
break;
case 'line':
xOffset = w ? w / 2 : Math.cos(rad) * radius;
yOffset = Math.sin(rad) * radius;
ctx.moveTo(x - xOffset, y - yOffset);
ctx.lineTo(x + xOffset, y + yOffset);
break;
case 'dash':
ctx.moveTo(x, y);
ctx.lineTo(x + Math.cos(rad) * (w ? w / 2 : radius), y + Math.sin(rad) * radius);
break;
case false:
ctx.closePath();
break;
}
ctx.fill();
if (options.borderWidth > 0) {
ctx.stroke();
}
}
/**
* Returns true if the point is inside the rectangle
* @param point - The point to test
* @param area - The rectangle
* @param margin - allowed margin
* @private
*/ function _isPointInArea(point, area, margin) {
margin = margin || 0.5; // margin - default is to match rounded decimals
return !area || point && point.x > area.left - margin && point.x < area.right + margin && point.y > area.top - margin && point.y < area.bottom + margin;
}
function clipArea(ctx, area) {
ctx.save();
ctx.beginPath();
ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);
ctx.clip();
}
function unclipArea(ctx) {
ctx.restore();
}
/**
* @private
*/ function _steppedLineTo(ctx, previous, target, flip, mode) {
if (!previous) {
return ctx.lineTo(target.x, target.y);
}
if (mode === 'middle') {
const midpoint = (previous.x + target.x) / 2.0;
ctx.lineTo(midpoint, previous.y);
ctx.lineTo(midpoint, target.y);
} else if (mode === 'after' !== !!flip) {
ctx.lineTo(previous.x, target.y);
} else {
ctx.lineTo(target.x, previous.y);
}
ctx.lineTo(target.x, target.y);
}
/**
* @private
*/ function _bezierCurveTo(ctx, previous, target, flip) {
if (!previous) {
return ctx.lineTo(target.x, target.y);
}
ctx.bezierCurveTo(flip ? previous.cp1x : previous.cp2x, flip ? previous.cp1y : previous.cp2y, flip ? target.cp2x : target.cp1x, flip ? target.cp2y : target.cp1y, target.x, target.y);
}
function setRenderOpts(ctx, opts) {
if (opts.translation) {
ctx.translate(opts.translation[0], opts.translation[1]);
}
if (!isNullOrUndef(opts.rotation)) {
ctx.rotate(opts.rotation);
}
if (opts.color) {
ctx.fillStyle = opts.color;
}
if (opts.textAlign) {
ctx.textAlign = opts.textAlign;
}
if (opts.textBaseline) {
ctx.textBaseline = opts.textBaseline;
}
}
function decorateText(ctx, x, y, line, opts) {
if (opts.strikethrough || opts.underline) {
/**
* Now that IE11 support has been dropped, we can use more
* of the TextMetrics object. The actual bounding boxes
* are unflagged in Chrome, Firefox, Edge, and Safari so they
* can be safely used.
* See https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics#Browser_compatibility
*/ const metrics = ctx.measureText(line);
const left = x - metrics.actualBoundingBoxLeft;
const right = x + metrics.actualBoundingBoxRight;
const top = y - metrics.actualBoundingBoxAscent;
const bottom = y + metrics.actualBoundingBoxDescent;
const yDecoration = opts.strikethrough ? (top + bottom) / 2 : bottom;
ctx.strokeStyle = ctx.fillStyle;
ctx.beginPath();
ctx.lineWidth = opts.decorationWidth || 2;
ctx.moveTo(left, yDecoration);
ctx.lineTo(right, yDecoration);
ctx.stroke();
}
}
function drawBackdrop(ctx, opts) {
const oldColor = ctx.fillStyle;
ctx.fillStyle = opts.color;
ctx.fillRect(opts.left, opts.top, opts.width, opts.height);
ctx.fillStyle = oldColor;
}
/**
* Render text onto the canvas
*/ function renderText(ctx, text, x, y, font, opts = {}) {
const lines = isArray(text) ? text : [
text
];
const stroke = opts.strokeWidth > 0 && opts.strokeColor !== '';
let i, line;
ctx.save();
ctx.font = font.string;
setRenderOpts(ctx, opts);
for(i = 0; i < lines.length; ++i){
line = lines[i];
if (opts.backdrop) {
drawBackdrop(ctx, opts.backdrop);
}
if (stroke) {
if (opts.strokeColor) {
ctx.strokeStyle = opts.strokeColor;
}
if (!isNullOrUndef(opts.strokeWidth)) {
ctx.lineWidth = opts.strokeWidth;
}
ctx.strokeText(line, x, y, opts.maxWidth);
}
ctx.fillText(line, x, y, opts.maxWidth);
decorateText(ctx, x, y, line, opts);
y += Number(font.lineHeight);
}
ctx.restore();
}
/**
* Add a path of a rectangle with rounded corners to the current sub-path
* @param ctx - Context
* @param rect - Bounding rect
*/ function addRoundedRectPath(ctx, rect) {
const { x , y , w , h , radius } = rect;
// top left arc
ctx.arc(x + radius.topLeft, y + radius.topLeft, radius.topLeft, 1.5 * PI, PI, true);
// line from top left to bottom left
ctx.lineTo(x, y + h - radius.bottomLeft);
// bottom left arc
ctx.arc(x + radius.bottomLeft, y + h - radius.bottomLeft, radius.bottomLeft, PI, HALF_PI, true);
// line from bottom left to bottom right
ctx.lineTo(x + w - radius.bottomRight, y + h);
// bottom right arc
ctx.arc(x + w - radius.bottomRight, y + h - radius.bottomRight, radius.bottomRight, HALF_PI, 0, true);
// line from bottom right to top right
ctx.lineTo(x + w, y + radius.topRight);
// top right arc
ctx.arc(x + w - radius.topRight, y + radius.topRight, radius.topRight, 0, -HALF_PI, true);
// line from top right to top left
ctx.lineTo(x + radius.topLeft, y);
}
const LINE_HEIGHT = /^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/;
const FONT_STYLE = /^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/;
/**
* @alias Chart.helpers.options
* @namespace
*/ /**
* Converts the given line height `value` in pixels for a specific font `size`.
* @param value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em').
* @param size - The font size (in pixels) used to resolve relative `value`.
* @returns The effective line height in pixels (size * 1.2 if value is invalid).
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
* @since 2.7.0
*/ function toLineHeight(value, size) {
const matches = ('' + value).match(LINE_HEIGHT);
if (!matches || matches[1] === 'normal') {
return size * 1.2;
}
value = +matches[2];
switch(matches[3]){
case 'px':
return value;
case '%':
value /= 100;
break;
}
return size * value;
}
const numberOrZero = (v)=>+v || 0;
function _readValueToProps(value, props) {
const ret = {};
const objProps = isObject(props);
const keys = objProps ? Object.keys(props) : props;
const read = isObject(value) ? objProps ? (prop)=>valueOrDefault(value[prop], value[props[prop]]) : (prop)=>value[prop] : ()=>value;
for (const prop of keys){
ret[prop] = numberOrZero(read(prop));
}
return ret;
}
/**
* Converts the given value into a TRBL object.
* @param value - If a number, set the value to all TRBL component,
* else, if an object, use defined properties and sets undefined ones to 0.
* x / y are shorthands for same value for left/right and top/bottom.
* @returns The padding values (top, right, bottom, left)
* @since 3.0.0
*/ function toTRBL(value) {
return _readValueToProps(value, {
top: 'y',
right: 'x',
bottom: 'y',
left: 'x'
});
}
/**
* Converts the given value into a TRBL corners object (similar with css border-radius).
* @param value - If a number, set the value to all TRBL corner components,
* else, if an object, use defined properties and sets undefined ones to 0.
* @returns The TRBL corner values (topLeft, topRight, bottomLeft, bottomRight)
* @since 3.0.0
*/ function toTRBLCorners(value) {
return _readValueToProps(value, [
'topLeft',
'topRight',
'bottomLeft',
'bottomRight'
]);
}
/**
* Converts the given value into a padding object with pre-computed width/height.
* @param value - If a number, set the value to all TRBL component,
* else, if an object, use defined properties and sets undefined ones to 0.
* x / y are shorthands for same value for left/right and top/bottom.
* @returns The padding values (top, right, bottom, left, width, height)
* @since 2.7.0
*/ function toPadding(value) {
const obj = toTRBL(value);
obj.width = obj.left + obj.right;
obj.height = obj.top + obj.bottom;
return obj;
}
/**
* Parses font options and returns the font object.
* @param options - A object that contains font options to be parsed.
* @param fallback - A object that contains fallback font options.
* @return The font object.
* @private
*/ function toFont(options, fallback) {
options = options || {};
fallback = fallback || defaults.font;
let size = valueOrDefault(options.size, fallback.size);
if (typeof size === 'string') {
size = parseInt(size, 10);
}
let style = valueOrDefault(options.style, fallback.style);
if (style && !('' + style).match(FONT_STYLE)) {
console.warn('Invalid font style specified: "' + style + '"');
style = undefined;
}
const font = {
family: valueOrDefault(options.family, fallback.family),
lineHeight: toLineHeight(valueOrDefault(options.lineHeight, fallback.lineHeight), size),
size,
style,
weight: valueOrDefault(options.weight, fallback.weight),
string: ''
};
font.string = toFontString(font);
return font;
}
/**
* Evaluates the given `inputs` sequentially and returns the first defined value.
* @param inputs - An array of values, falling back to the last value.
* @param context - If defined and the current value is a function, the value
* is called with `context` as first argument and the result becomes the new input.
* @param index - If defined and the current value is an array, the value
* at `index` become the new input.
* @param info - object to return information about resolution in
* @param info.cacheable - Will be set to `false` if option is not cacheable.
* @since 2.7.0
*/ function resolve(inputs, context, index, info) {
let cacheable = true;
let i, ilen, value;
for(i = 0, ilen = inputs.length; i < ilen; ++i){
value = inputs[i];
if (value === undefined) {
continue;
}
if (context !== undefined && typeof value === 'function') {
value = value(context);
cacheable = false;
}
if (index !== undefined && isArray(value)) {
value = value[index % value.length];
cacheable = false;
}
if (value !== undefined) {
if (info && !cacheable) {
info.cacheable = false;
}
return value;
}
}
}
/**
* @param minmax
* @param grace
* @param beginAtZero
* @private
*/ function _addGrace(minmax, grace, beginAtZero) {
const { min , max } = minmax;
const change = toDimension(grace, (max - min) / 2);
const keepZero = (value, add)=>beginAtZero && value === 0 ? 0 : value + add;
return {
min: keepZero(min, -Math.abs(change)),
max: keepZero(max, change)
};
}
function createContext(parentContext, context) {
return Object.assign(Object.create(parentContext), context);
}
/**
* Creates a Proxy for resolving raw values for options.
* @param scopes - The option scopes to look for values, in resolution order
* @param prefixes - The prefixes for values, in resolution order.
* @param rootScopes - The root option scopes
* @param fallback - Parent scopes fallback
* @param getTarget - callback for getting the target for changed values
* @returns Proxy
* @private
*/ function _createResolver(scopes, prefixes = [
''
], rootScopes, fallback, getTarget = ()=>scopes[0]) {
const finalRootScopes = rootScopes || scopes;
if (typeof fallback === 'undefined') {
fallback = _resolve('_fallback', scopes);
}
const cache = {
[Symbol.toStringTag]: 'Object',
_cacheable: true,
_scopes: scopes,
_rootScopes: finalRootScopes,
_fallback: fallback,
_getTarget: getTarget,
override: (scope)=>_createResolver([
scope,
...scopes
], prefixes, finalRootScopes, fallback)
};
return new Proxy(cache, {
/**
* A trap for the delete operator.
*/ deleteProperty (target, prop) {
delete target[prop]; // remove from cache
delete target._keys; // remove cached keys
delete scopes[0][prop]; // remove from top level scope
return true;
},
/**
* A trap for getting property values.
*/ get (target, prop) {
return _cached(target, prop, ()=>_resolveWithPrefixes(prop, prefixes, scopes, target));
},
/**
* A trap for Object.getOwnPropertyDescriptor.
* Also used by Object.hasOwnProperty.
*/ getOwnPropertyDescriptor (target, prop) {
return Reflect.getOwnPropertyDescriptor(target._scopes[0], prop);
},
/**
* A trap for Object.getPrototypeOf.
*/ getPrototypeOf () {
return Reflect.getPrototypeOf(scopes[0]);
},
/**
* A trap for the in operator.
*/ has (target, prop) {
return getKeysFromAllScopes(target).includes(prop);
},
/**
* A trap for Object.getOwnPropertyNames and Object.getOwnPropertySymbols.
*/ ownKeys (target) {
return getKeysFromAllScopes(target);
},
/**
* A trap for setting property values.
*/ set (target, prop, value) {
const storage = target._storage || (target._storage = getTarget());
target[prop] = storage[prop] = value; // set to top level scope + cache
delete target._keys; // remove cached keys
return true;
}
});
}
/**
* Returns an Proxy for resolving option values with context.
* @param proxy - The Proxy returned by `_createResolver`
* @param context - Context object for scriptable/indexable options
* @param subProxy - The proxy provided for scriptable options
* @param descriptorDefaults - Defaults for descriptors
* @private
*/ function _attachContext(proxy, context, subProxy, descriptorDefaults) {
const cache = {
_cacheable: false,
_proxy: proxy,
_context: context,
_subProxy: subProxy,
_stack: new Set(),
_descriptors: _descriptors(proxy, descriptorDefaults),
setContext: (ctx)=>_attachContext(proxy, ctx, subProxy, descriptorDefaults),
override: (scope)=>_attachContext(proxy.override(scope), context, subProxy, descriptorDefaults)
};
return new Proxy(cache, {
/**
* A trap for the delete operator.
*/ deleteProperty (target, prop) {
delete target[prop]; // remove from cache
delete proxy[prop]; // remove from proxy
return true;
},
/**
* A trap for getting property values.
*/ get (target, prop, receiver) {
return _cached(target, prop, ()=>_resolveWithContext(target, prop, receiver));
},
/**
* A trap for Object.getOwnPropertyDescriptor.
* Also used by Object.hasOwnProperty.
*/ getOwnPropertyDescriptor (target, prop) {
return target._descriptors.allKeys ? Reflect.has(proxy, prop) ? {
enumerable: true,
configurable: true
} : undefined : Reflect.getOwnPropertyDescriptor(proxy, prop);
},
/**
* A trap for Object.getPrototypeOf.
*/ getPrototypeOf () {
return Reflect.getPrototypeOf(proxy);
},
/**
* A trap for the in operator.
*/ has (target, prop) {
return Reflect.has(proxy, prop);
},
/**
* A trap for Object.getOwnPropertyNames and Object.getOwnPropertySymbols.
*/ ownKeys () {
return Reflect.ownKeys(proxy);
},
/**
* A trap for setting property values.
*/ set (target, prop, value) {
proxy[prop] = value; // set to proxy
delete target[prop]; // remove from cache
return true;
}
});
}
/**
* @private
*/ function _descriptors(proxy, defaults = {
scriptable: true,
indexable: true
}) {
const { _scriptable =defaults.scriptable , _indexable =defaults.indexable , _allKeys =defaults.allKeys } = proxy;
return {
allKeys: _allKeys,
scriptable: _scriptable,
indexable: _indexable,
isScriptable: isFunction(_scriptable) ? _scriptable : ()=>_scriptable,
isIndexable: isFunction(_indexable) ? _indexable : ()=>_indexable
};
}
const readKey = (prefix, name)=>prefix ? prefix + _capitalize(name) : name;
const needsSubResolver = (prop, value)=>isObject(value) && prop !== 'adapters' && (Object.getPrototypeOf(value) === null || value.constructor === Object);
function _cached(target, prop, resolve) {
if (Object.prototype.hasOwnProperty.call(target, prop) || prop === 'constructor') {
return target[prop];
}
const value = resolve();
// cache the resolved value
target[prop] = value;
return value;
}
function _resolveWithContext(target, prop, receiver) {
const { _proxy , _context , _subProxy , _descriptors: descriptors } = target;
let value = _proxy[prop]; // resolve from proxy
// resolve with context
if (isFunction(value) && descriptors.isScriptable(prop)) {
value = _resolveScriptable(prop, value, target, receiver);
}
if (isArray(value) && value.length) {
value = _resolveArray(prop, value, target, descriptors.isIndexable);
}
if (needsSubResolver(prop, value)) {
// if the resolved value is an object, create a sub resolver for it
value = _attachContext(value, _context, _subProxy && _subProxy[prop], descriptors);
}
return value;
}
function _resolveScriptable(prop, getValue, target, receiver) {
const { _proxy , _context , _subProxy , _stack } = target;
if (_stack.has(prop)) {
throw new Error('Recursion detected: ' + Array.from(_stack).join('->') + '->' + prop);
}
_stack.add(prop);
let value = getValue(_context, _subProxy || receiver);
_stack.delete(prop);
if (needsSubResolver(prop, value)) {
// When scriptable option returns an object, create a resolver on that.
value = createSubResolver(_proxy._scopes, _proxy, prop, value);
}
return value;
}
function _resolveArray(prop, value, target, isIndexable) {
const { _proxy , _context , _subProxy , _descriptors: descriptors } = target;
if (typeof _context.index !== 'undefined' && isIndexable(prop)) {
return value[_context.index % value.length];
} else if (isObject(value[0])) {
// Array of objects, return array or resolvers
const arr = value;
const scopes = _proxy._scopes.filter((s)=>s !== arr);
value = [];
for (const item of arr){
const resolver = createSubResolver(scopes, _proxy, prop, item);
value.push(_attachContext(resolver, _context, _subProxy && _subProxy[prop], descriptors));
}
}
return value;
}
function resolveFallback(fallback, prop, value) {
return isFunction(fallback) ? fallback(prop, value) : fallback;
}
const getScope = (key, parent)=>key === true ? parent : typeof key === 'string' ? resolveObjectKey(parent, key) : undefined;
function addScopes(set, parentScopes, key, parentFallback, value) {
for (const parent of parentScopes){
const scope = getScope(key, parent);
if (scope) {
set.add(scope);
const fallback = resolveFallback(scope._fallback, key, value);
if (typeof fallback !== 'undefined' && fallback !== key && fallback !== parentFallback) {
// When we reach the descriptor that defines a new _fallback, return that.
// The fallback will resume to that new scope.
return fallback;
}
} else if (scope === false && typeof parentFallback !== 'undefined' && key !== parentFallback) {
// Fallback to `false` results to `false`, when falling back to different key.
// For example `interaction` from `hover` or `plugins.tooltip` and `animation` from `animations`
return null;
}
}
return false;
}
function createSubResolver(parentScopes, resolver, prop, value) {
const rootScopes = resolver._rootScopes;
const fallback = resolveFallback(resolver._fallback, prop, value);
const allScopes = [
...parentScopes,
...rootScopes
];
const set = new Set();
set.add(value);
let key = addScopesFromKey(set, allScopes, prop, fallback || prop, value);
if (key === null) {
return false;
}
if (typeof fallback !== 'undefined' && fallback !== prop) {
key = addScopesFromKey(set, allScopes, fallback, key, value);
if (key === null) {
return false;
}
}
return _createResolver(Array.from(set), [
''
], rootScopes, fallback, ()=>subGetTarget(resolver, prop, value));
}
function addScopesFromKey(set, allScopes, key, fallback, item) {
while(key){
key = addScopes(set, allScopes, key, fallback, item);
}
return key;
}
function subGetTarget(resolver, prop, value) {
const parent = resolver._getTarget();
if (!(prop in parent)) {
parent[prop] = {};
}
const target = parent[prop];
if (isArray(target) && isObject(value)) {
// For array of objects, the object is used to store updated values
return value;
}
return target || {};
}
function _resolveWithPrefixes(prop, prefixes, scopes, proxy) {
let value;
for (const prefix of prefixes){
value = _resolve(readKey(prefix, prop), scopes);
if (typeof value !== 'undefined') {
return needsSubResolver(prop, value) ? createSubResolver(scopes, proxy, prop, value) : value;
}
}
}
function _resolve(key, scopes) {
for (const scope of scopes){
if (!scope) {
continue;
}
const value = scope[key];
if (typeof value !== 'undefined') {
return value;
}
}
}
function getKeysFromAllScopes(target) {
let keys = target._keys;
if (!keys) {
keys = target._keys = resolveKeysFromAllScopes(target._scopes);
}
return keys;
}
function resolveKeysFromAllScopes(scopes) {
const set = new Set();
for (const scope of scopes){
for (const key of Object.keys(scope).filter((k)=>!k.startsWith('_'))){
set.add(key);
}
}
return Array.from(set);
}
function _parseObjectDataRadialScale(meta, data, start, count) {
const { iScale } = meta;
const { key ='r' } = this._parsing;
const parsed = new Array(count);
let i, ilen, index, item;
for(i = 0, ilen = count; i < ilen; ++i){
index = i + start;
item = data[index];
parsed[i] = {
r: iScale.parse(resolveObjectKey(item, key), index)
};
}
return parsed;
}
const EPSILON = Number.EPSILON || 1e-14;
const getPoint = (points, i)=>i < points.length && !points[i].skip && points[i];
const getValueAxis = (indexAxis)=>indexAxis === 'x' ? 'y' : 'x';
function splineCurve(firstPoint, middlePoint, afterPoint, t) {
// Props to Rob Spencer at scaled innovation for his post on splining between points
// http://scaledinnovation.com/analytics/splines/aboutSplines.html
// This function must also respect "skipped" points
const previous = firstPoint.skip ? middlePoint : firstPoint;
const current = middlePoint;
const next = afterPoint.skip ? middlePoint : afterPoint;
const d01 = distanceBetweenPoints(current, previous);
const d12 = distanceBetweenPoints(next, current);
let s01 = d01 / (d01 + d12);
let s12 = d12 / (d01 + d12);
// If all points are the same, s01 & s02 will be inf
s01 = isNaN(s01) ? 0 : s01;
s12 = isNaN(s12) ? 0 : s12;
const fa = t * s01; // scaling factor for triangle Ta
const fb = t * s12;
return {
previous: {
x: current.x - fa * (next.x - previous.x),
y: current.y - fa * (next.y - previous.y)
},
next: {
x: current.x + fb * (next.x - previous.x),
y: current.y + fb * (next.y - previous.y)
}
};
}
/**
* Adjust tangents to ensure monotonic properties
*/ function monotoneAdjust(points, deltaK, mK) {
const pointsLen = points.length;
let alphaK, betaK, tauK, squaredMagnitude, pointCurrent;
let pointAfter = getPoint(points, 0);
for(let i = 0; i < pointsLen - 1; ++i){
pointCurrent = pointAfter;
pointAfter = getPoint(points, i + 1);
if (!pointCurrent || !pointAfter) {
continue;
}
if (almostEquals(deltaK[i], 0, EPSILON)) {
mK[i] = mK[i + 1] = 0;
continue;
}
alphaK = mK[i] / deltaK[i];
betaK = mK[i + 1] / deltaK[i];
squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2);
if (squaredMagnitude <= 9) {
continue;
}
tauK = 3 / Math.sqrt(squaredMagnitude);
mK[i] = alphaK * tauK * deltaK[i];
mK[i + 1] = betaK * tauK * deltaK[i];
}
}
function monotoneCompute(points, mK, indexAxis = 'x') {
const valueAxis = getValueAxis(indexAxis);
const pointsLen = points.length;
let delta, pointBefore, pointCurrent;
let pointAfter = getPoint(points, 0);
for(let i = 0; i < pointsLen; ++i){
pointBefore = pointCurrent;
pointCurrent = pointAfter;
pointAfter = getPoint(points, i + 1);
if (!pointCurrent) {
continue;
}
const iPixel = pointCurrent[indexAxis];
const vPixel = pointCurrent[valueAxis];
if (pointBefore) {
delta = (iPixel - pointBefore[indexAxis]) / 3;
pointCurrent[`cp1${indexAxis}`] = iPixel - delta;
pointCurrent[`cp1${valueAxis}`] = vPixel - delta * mK[i];
}
if (pointAfter) {
delta = (pointAfter[indexAxis] - iPixel) / 3;
pointCurrent[`cp2${indexAxis}`] = iPixel + delta;
pointCurrent[`cp2${valueAxis}`] = vPixel + delta * mK[i];
}
}
}
/**
* This function calculates Bézier control points in a similar way than |splineCurve|,
* but preserves monotonicity of the provided data and ensures no local extremums are added
* between the dataset discrete points due to the interpolation.
* See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation
*/ function splineCurveMonotone(points, indexAxis = 'x') {
const valueAxis = getValueAxis(indexAxis);
const pointsLen = points.length;
const deltaK = Array(pointsLen).fill(0);
const mK = Array(pointsLen);
// Calculate slopes (deltaK) and initialize tangents (mK)
let i, pointBefore, pointCurrent;
let pointAfter = getPoint(points, 0);
for(i = 0; i < pointsLen; ++i){
pointBefore = pointCurrent;
pointCurrent = pointAfter;
pointAfter = getPoint(points, i + 1);
if (!pointCurrent) {
continue;
}
if (pointAfter) {
const slopeDelta = pointAfter[indexAxis] - pointCurrent[indexAxis];
// In the case of two points that appear at the same x pixel, slopeDeltaX is 0
deltaK[i] = slopeDelta !== 0 ? (pointAfter[valueAxis] - pointCurrent[valueAxis]) / slopeDelta : 0;
}
mK[i] = !pointBefore ? deltaK[i] : !pointAfter ? deltaK[i - 1] : sign(deltaK[i - 1]) !== sign(deltaK[i]) ? 0 : (deltaK[i - 1] + deltaK[i]) / 2;
}
monotoneAdjust(points, deltaK, mK);
monotoneCompute(points, mK, indexAxis);
}
function capControlPoint(pt, min, max) {
return Math.max(Math.min(pt, max), min);
}
function capBezierPoints(points, area) {
let i, ilen, point, inArea, inAreaPrev;
let inAreaNext = _isPointInArea(points[0], area);
for(i = 0, ilen = points.length; i < ilen; ++i){
inAreaPrev = inArea;
inArea = inAreaNext;
inAreaNext = i < ilen - 1 && _isPointInArea(points[i + 1], area);
if (!inArea) {
continue;
}
point = points[i];
if (inAreaPrev) {
point.cp1x = capControlPoint(point.cp1x, area.left, area.right);
point.cp1y = capControlPoint(point.cp1y, area.top, area.bottom);
}
if (inAreaNext) {
point.cp2x = capControlPoint(point.cp2x, area.left, area.right);
point.cp2y = capControlPoint(point.cp2y, area.top, area.bottom);
}
}
}
/**
* @private
*/ function _updateBezierControlPoints(points, options, area, loop, indexAxis) {
let i, ilen, point, controlPoints;
// Only consider points that are drawn in case the spanGaps option is used
if (options.spanGaps) {
points = points.filter((pt)=>!pt.skip);
}
if (options.cubicInterpolationMode === 'monotone') {
splineCurveMonotone(points, indexAxis);
} else {
let prev = loop ? points[points.length - 1] : points[0];
for(i = 0, ilen = points.length; i < ilen; ++i){
point = points[i];
controlPoints = splineCurve(prev, point, points[Math.min(i + 1, ilen - (loop ? 0 : 1)) % ilen], options.tension);
point.cp1x = controlPoints.previous.x;
point.cp1y = controlPoints.previous.y;
point.cp2x = controlPoints.next.x;
point.cp2y = controlPoints.next.y;
prev = point;
}
}
if (options.capBezierPoints) {
capBezierPoints(points, area);
}
}
/**
* @private
*/ function _isDomSupported() {
return typeof window !== 'undefined' && typeof document !== 'undefined';
}
/**
* @private
*/ function _getParentNode(domNode) {
let parent = domNode.parentNode;
if (parent && parent.toString() === '[object ShadowRoot]') {
parent = parent.host;
}
return parent;
}
/**
* convert max-width/max-height values that may be percentages into a number
* @private
*/ function parseMaxStyle(styleValue, node, parentProperty) {
let valueInPixels;
if (typeof styleValue === 'string') {
valueInPixels = parseInt(styleValue, 10);
if (styleValue.indexOf('%') !== -1) {
// percentage * size in dimension
valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty];
}
} else {
valueInPixels = styleValue;
}
return valueInPixels;
}
const getComputedStyle = (element)=>element.ownerDocument.defaultView.getComputedStyle(element, null);
function getStyle(el, property) {
return getComputedStyle(el).getPropertyValue(property);
}
const positions = [
'top',
'right',
'bottom',
'left'
];
function getPositionedStyle(styles, style, suffix) {
const result = {};
suffix = suffix ? '-' + suffix : '';
for(let i = 0; i < 4; i++){
const pos = positions[i];
result[pos] = parseFloat(styles[style + '-' + pos + suffix]) || 0;
}
result.width = result.left + result.right;
result.height = result.top + result.bottom;
return result;
}
const useOffsetPos = (x, y, target)=>(x > 0 || y > 0) && (!target || !target.shadowRoot);
/**
* @param e
* @param canvas
* @returns Canvas position
*/ function getCanvasPosition(e, canvas) {
const touches = e.touches;
const source = touches && touches.length ? touches[0] : e;
const { offsetX , offsetY } = source;
let box = false;
let x, y;
if (useOffsetPos(offsetX, offsetY, e.target)) {
x = offsetX;
y = offsetY;
} else {
const rect = canvas.getBoundingClientRect();
x = source.clientX - rect.left;
y = source.clientY - rect.top;
box = true;
}
return {
x,
y,
box
};
}
/**
* Gets an event's x, y coordinates, relative to the chart area
* @param event
* @param chart
* @returns x and y coordinates of the event
*/ function getRelativePosition(event, chart) {
if ('native' in event) {
return event;
}
const { canvas , currentDevicePixelRatio } = chart;
const style = getComputedStyle(canvas);
const borderBox = style.boxSizing === 'border-box';
const paddings = getPositionedStyle(style, 'padding');
const borders = getPositionedStyle(style, 'border', 'width');
const { x , y , box } = getCanvasPosition(event, canvas);
const xOffset = paddings.left + (box && borders.left);
const yOffset = paddings.top + (box && borders.top);
let { width , height } = chart;
if (borderBox) {
width -= paddings.width + borders.width;
height -= paddings.height + borders.height;
}
return {
x: Math.round((x - xOffset) / width * canvas.width / currentDevicePixelRatio),
y: Math.round((y - yOffset) / height * canvas.height / currentDevicePixelRatio)
};
}
function getContainerSize(canvas, width, height) {
let maxWidth, maxHeight;
if (width === undefined || height === undefined) {
const container = canvas && _getParentNode(canvas);
if (!container) {
width = canvas.clientWidth;
height = canvas.clientHeight;
} else {
const rect = container.getBoundingClientRect(); // this is the border box of the container
const containerStyle = getComputedStyle(container);
const containerBorder = getPositionedStyle(containerStyle, 'border', 'width');
const containerPadding = getPositionedStyle(containerStyle, 'padding');
width = rect.width - containerPadding.width - containerBorder.width;
height = rect.height - containerPadding.height - containerBorder.height;
maxWidth = parseMaxStyle(containerStyle.maxWidth, container, 'clientWidth');
maxHeight = parseMaxStyle(containerStyle.maxHeight, container, 'clientHeight');
}
}
return {
width,
height,
maxWidth: maxWidth || INFINITY,
maxHeight: maxHeight || INFINITY
};
}
const round1 = (v)=>Math.round(v * 10) / 10;
// eslint-disable-next-line complexity
function getMaximumSize(canvas, bbWidth, bbHeight, aspectRatio) {
const style = getComputedStyle(canvas);
const margins = getPositionedStyle(style, 'margin');
const maxWidth = parseMaxStyle(style.maxWidth, canvas, 'clientWidth') || INFINITY;
const maxHeight = parseMaxStyle(style.maxHeight, canvas, 'clientHeight') || INFINITY;
const containerSize = getContainerSize(canvas, bbWidth, bbHeight);
let { width , height } = containerSize;
if (style.boxSizing === 'content-box') {
const borders = getPositionedStyle(style, 'border', 'width');
const paddings = getPositionedStyle(style, 'padding');
width -= paddings.width + borders.width;
height -= paddings.height + borders.height;
}
width = Math.max(0, width - margins.width);
height = Math.max(0, aspectRatio ? width / aspectRatio : height - margins.height);
width = round1(Math.min(width, maxWidth, containerSize.maxWidth));
height = round1(Math.min(height, maxHeight, containerSize.maxHeight));
if (width && !height) {
// https://github.com/chartjs/Chart.js/issues/4659
// If the canvas has width, but no height, default to aspectRatio of 2 (canvas default)
height = round1(width / 2);
}
const maintainHeight = bbWidth !== undefined || bbHeight !== undefined;
if (maintainHeight && aspectRatio && containerSize.height && height > containerSize.height) {
height = containerSize.height;
width = round1(Math.floor(height * aspectRatio));
}
return {
width,
height
};
}
/**
* @param chart
* @param forceRatio
* @param forceStyle
* @returns True if the canvas context size or transformation has changed.
*/ function retinaScale(chart, forceRatio, forceStyle) {
const pixelRatio = forceRatio || 1;
const deviceHeight = round1(chart.height * pixelRatio);
const deviceWidth = round1(chart.width * pixelRatio);
chart.height = round1(chart.height);
chart.width = round1(chart.width);
const canvas = chart.canvas;
// If no style has been set on the canvas, the render size is used as display size,
// making the chart visually bigger, so let's enforce it to the "correct" values.
// See https://github.com/chartjs/Chart.js/issues/3575
if (canvas.style && (forceStyle || !canvas.style.height && !canvas.style.width)) {
canvas.style.height = `${chart.height}px`;
canvas.style.width = `${chart.width}px`;
}
if (chart.currentDevicePixelRatio !== pixelRatio || canvas.height !== deviceHeight || canvas.width !== deviceWidth) {
chart.currentDevicePixelRatio = pixelRatio;
canvas.height = deviceHeight;
canvas.width = deviceWidth;
chart.ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
return true;
}
return false;
}
/**
* Detects support for options object argument in addEventListener.
* https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
* @private
*/ const supportsEventListenerOptions = function() {
let passiveSupported = false;
try {
const options = {
get passive () {
passiveSupported = true;
return false;
}
};
if (_isDomSupported()) {
window.addEventListener('test', null, options);
window.removeEventListener('test', null, options);
}
} catch (e) {
// continue regardless of error
}
return passiveSupported;
}();
/**
* The "used" size is the final value of a dimension property after all calculations have
* been performed. This method uses the computed style of `element` but returns undefined
* if the computed style is not expressed in pixels. That can happen in some cases where
* `element` has a size relative to its parent and this last one is not yet displayed,
* for example because of `display: none` on a parent node.
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
* @returns Size in pixels or undefined if unknown.
*/ function readUsedSize(element, property) {
const value = getStyle(element, property);
const matches = value && value.match(/^(\d+)(\.\d+)?px$/);
return matches ? +matches[1] : undefined;
}
/**
* @private
*/ function _pointInLine(p1, p2, t, mode) {
return {
x: p1.x + t * (p2.x - p1.x),
y: p1.y + t * (p2.y - p1.y)
};
}
/**
* @private
*/ function _steppedInterpolation(p1, p2, t, mode) {
return {
x: p1.x + t * (p2.x - p1.x),
y: mode === 'middle' ? t < 0.5 ? p1.y : p2.y : mode === 'after' ? t < 1 ? p1.y : p2.y : t > 0 ? p2.y : p1.y
};
}
/**
* @private
*/ function _bezierInterpolation(p1, p2, t, mode) {
const cp1 = {
x: p1.cp2x,
y: p1.cp2y
};
const cp2 = {
x: p2.cp1x,
y: p2.cp1y
};
const a = _pointInLine(p1, cp1, t);
const b = _pointInLine(cp1, cp2, t);
const c = _pointInLine(cp2, p2, t);
const d = _pointInLine(a, b, t);
const e = _pointInLine(b, c, t);
return _pointInLine(d, e, t);
}
const getRightToLeftAdapter = function(rectX, width) {
return {
x (x) {
return rectX + rectX + width - x;
},
setWidth (w) {
width = w;
},
textAlign (align) {
if (align === 'center') {
return align;
}
return align === 'right' ? 'left' : 'right';
},
xPlus (x, value) {
return x - value;
},
leftForLtr (x, itemWidth) {
return x - itemWidth;
}
};
};
const getLeftToRightAdapter = function() {
return {
x (x) {
return x;
},
setWidth (w) {},
textAlign (align) {
return align;
},
xPlus (x, value) {
return x + value;
},
leftForLtr (x, _itemWidth) {
return x;
}
};
};
function getRtlAdapter(rtl, rectX, width) {
return rtl ? getRightToLeftAdapter(rectX, width) : getLeftToRightAdapter();
}
function overrideTextDirection(ctx, direction) {
let style, original;
if (direction === 'ltr' || direction === 'rtl') {
style = ctx.canvas.style;
original = [
style.getPropertyValue('direction'),
style.getPropertyPriority('direction')
];
style.setProperty('direction', direction, 'important');
ctx.prevTextDirection = original;
}
}
function restoreTextDirection(ctx, original) {
if (original !== undefined) {
delete ctx.prevTextDirection;
ctx.canvas.style.setProperty('direction', original[0], original[1]);
}
}
function propertyFn(property) {
if (property === 'angle') {
return {
between: _angleBetween,
compare: _angleDiff,
normalize: _normalizeAngle
};
}
return {
between: _isBetween,
compare: (a, b)=>a - b,
normalize: (x)=>x
};
}
function normalizeSegment({ start , end , count , loop , style }) {
return {
start: start % count,
end: end % count,
loop: loop && (end - start + 1) % count === 0,
style
};
}
function getSegment(segment, points, bounds) {
const { property , start: startBound , end: endBound } = bounds;
const { between , normalize } = propertyFn(property);
const count = points.length;
let { start , end , loop } = segment;
let i, ilen;
if (loop) {
start += count;
end += count;
for(i = 0, ilen = count; i < ilen; ++i){
if (!between(normalize(points[start % count][property]), startBound, endBound)) {
break;
}
start--;
end--;
}
start %= count;
end %= count;
}
if (end < start) {
end += count;
}
return {
start,
end,
loop,
style: segment.style
};
}
function _boundSegment(segment, points, bounds) {
if (!bounds) {
return [
segment
];
}
const { property , start: startBound , end: endBound } = bounds;
const count = points.length;
const { compare , between , normalize } = propertyFn(property);
const { start , end , loop , style } = getSegment(segment, points, bounds);
const result = [];
let inside = false;
let subStart = null;
let value, point, prevValue;
const startIsBefore = ()=>between(startBound, prevValue, value) && compare(startBound, prevValue) !== 0;
const endIsBefore = ()=>compare(endBound, value) === 0 || between(endBound, prevValue, value);
const shouldStart = ()=>inside || startIsBefore();
const shouldStop = ()=>!inside || endIsBefore();
for(let i = start, prev = start; i <= end; ++i){
point = points[i % count];
if (point.skip) {
continue;
}
value = normalize(point[property]);
if (value === prevValue) {
continue;
}
inside = between(value, startBound, endBound);
if (subStart === null && shouldStart()) {
subStart = compare(value, startBound) === 0 ? i : prev;
}
if (subStart !== null && shouldStop()) {
result.push(normalizeSegment({
start: subStart,
end: i,
loop,
count,
style
}));
subStart = null;
}
prev = i;
prevValue = value;
}
if (subStart !== null) {
result.push(normalizeSegment({
start: subStart,
end,
loop,
count,
style
}));
}
return result;
}
function _boundSegments(line, bounds) {
const result = [];
const segments = line.segments;
for(let i = 0; i < segments.length; i++){
const sub = _boundSegment(segments[i], line.points, bounds);
if (sub.length) {
result.push(...sub);
}
}
return result;
}
function findStartAndEnd(points, count, loop, spanGaps) {
let start = 0;
let end = count - 1;
if (loop && !spanGaps) {
while(start < count && !points[start].skip){
start++;
}
}
while(start < count && points[start].skip){
start++;
}
start %= count;
if (loop) {
end += start;
}
while(end > start && points[end % count].skip){
end--;
}
end %= count;
return {
start,
end
};
}
function solidSegments(points, start, max, loop) {
const count = points.length;
const result = [];
let last = start;
let prev = points[start];
let end;
for(end = start + 1; end <= max; ++end){
const cur = points[end % count];
if (cur.skip || cur.stop) {
if (!prev.skip) {
loop = false;
result.push({
start: start % count,
end: (end - 1) % count,
loop
});
start = last = cur.stop ? end : null;
}
} else {
last = end;
if (prev.skip) {
start = end;
}
}
prev = cur;
}
if (last !== null) {
result.push({
start: start % count,
end: last % count,
loop
});
}
return result;
}
function _computeSegments(line, segmentOptions) {
const points = line.points;
const spanGaps = line.options.spanGaps;
const count = points.length;
if (!count) {
return [];
}
const loop = !!line._loop;
const { start , end } = findStartAndEnd(points, count, loop, spanGaps);
if (spanGaps === true) {
return splitByStyles(line, [
{
start,
end,
loop
}
], points, segmentOptions);
}
const max = end < start ? end + count : end;
const completeLoop = !!line._fullLoop && start === 0 && end === count - 1;
return splitByStyles(line, solidSegments(points, start, max, completeLoop), points, segmentOptions);
}
function splitByStyles(line, segments, points, segmentOptions) {
if (!segmentOptions || !segmentOptions.setContext || !points) {
return segments;
}
return doSplitByStyles(line, segments, points, segmentOptions);
}
function doSplitByStyles(line, segments, points, segmentOptions) {
const chartContext = line._chart.getContext();
const baseStyle = readStyle(line.options);
const { _datasetIndex: datasetIndex , options: { spanGaps } } = line;
const count = points.length;
const result = [];
let prevStyle = baseStyle;
let start = segments[0].start;
let i = start;
function addStyle(s, e, l, st) {
const dir = spanGaps ? -1 : 1;
if (s === e) {
return;
}
s += count;
while(points[s % count].skip){
s -= dir;
}
while(points[e % count].skip){
e += dir;
}
if (s % count !== e % count) {
result.push({
start: s % count,
end: e % count,
loop: l,
style: st
});
prevStyle = st;
start = e % count;
}
}
for (const segment of segments){
start = spanGaps ? start : segment.start;
let prev = points[start % count];
let style;
for(i = start + 1; i <= segment.end; i++){
const pt = points[i % count];
style = readStyle(segmentOptions.setContext(createContext(chartContext, {
type: 'segment',
p0: prev,
p1: pt,
p0DataIndex: (i - 1) % count,
p1DataIndex: i % count,
datasetIndex
})));
if (styleChanged(style, prevStyle)) {
addStyle(start, i - 1, segment.loop, prevStyle);
}
prev = pt;
prevStyle = style;
}
if (start < i - 1) {
addStyle(start, i - 1, segment.loop, prevStyle);
}
}
return result;
}
function readStyle(options) {
return {
backgroundColor: options.backgroundColor,
borderCapStyle: options.borderCapStyle,
borderDash: options.borderDash,
borderDashOffset: options.borderDashOffset,
borderJoinStyle: options.borderJoinStyle,
borderWidth: options.borderWidth,
borderColor: options.borderColor
};
}
function styleChanged(style, prevStyle) {
if (!prevStyle) {
return false;
}
const cache = [];
const replacer = function(key, value) {
if (!isPatternOrGradient(value)) {
return value;
}
if (!cache.includes(value)) {
cache.push(value);
}
return cache.indexOf(value);
};
return JSON.stringify(style, replacer) !== JSON.stringify(prevStyle, replacer);
}
function getSizeForArea(scale, chartArea, field) {
return scale.options.clip ? scale[field] : chartArea[field];
}
function getDatasetArea(meta, chartArea) {
const { xScale , yScale } = meta;
if (xScale && yScale) {
return {
left: getSizeForArea(xScale, chartArea, 'left'),
right: getSizeForArea(xScale, chartArea, 'right'),
top: getSizeForArea(yScale, chartArea, 'top'),
bottom: getSizeForArea(yScale, chartArea, 'bottom')
};
}
return chartArea;
}
function getDatasetClipArea(chart, meta) {
const clip = meta._clip;
if (clip.disabled) {
return false;
}
const area = getDatasetArea(meta, chart.chartArea);
return {
left: clip.left === false ? 0 : area.left - (clip.left === true ? 0 : clip.left),
right: clip.right === false ? chart.width : area.right + (clip.right === true ? 0 : clip.right),
top: clip.top === false ? 0 : area.top - (clip.top === true ? 0 : clip.top),
bottom: clip.bottom === false ? chart.height : area.bottom + (clip.bottom === true ? 0 : clip.bottom)
};
}
export { unclipArea as $, _rlookupByKey as A, _lookupByKey as B, _isPointInArea as C, getAngleFromPoint as D, toPadding as E, each as F, getMaximumSize as G, HALF_PI as H, _getParentNode as I, readUsedSize as J, supportsEventListenerOptions as K, throttled as L, _isDomSupported as M, _factorize as N, finiteOrDefault as O, PI as P, callback as Q, _addGrace as R, _limitValue as S, TAU as T, toDegrees as U, _measureText as V, _int16Range as W, _alignPixel as X, clipArea as Y, renderText as Z, _arrayUnique as _, resolve as a, getStyle as a$, toFont as a0, _toLeftRightCenter as a1, _alignStartEnd as a2, overrides as a3, merge as a4, _capitalize as a5, descriptors as a6, isFunction as a7, _attachContext as a8, _createResolver as a9, getRtlAdapter as aA, overrideTextDirection as aB, _textX as aC, restoreTextDirection as aD, drawPointLegend as aE, distanceBetweenPoints as aF, noop as aG, _setMinAndMaxByKey as aH, niceNum as aI, almostWhole as aJ, almostEquals as aK, _decimalPlaces as aL, Ticks as aM, log10 as aN, _longestText as aO, _filterBetween as aP, _lookup as aQ, isPatternOrGradient as aR, getHoverColor as aS, clone as aT, _merger as aU, _mergerIf as aV, _deprecated as aW, _splitKey as aX, toFontString as aY, splineCurve as aZ, splineCurveMonotone as a_, _descriptors as aa, mergeIf as ab, uid as ac, debounce as ad, retinaScale as ae, clearCanvas as af, setsEqual as ag, getDatasetClipArea as ah, _elementsEqual as ai, _isClickEvent as aj, _isBetween as ak, _normalizeAngle as al, _readValueToProps as am, _updateBezierControlPoints as an, _computeSegments as ao, _boundSegments as ap, _steppedInterpolation as aq, _bezierInterpolation as ar, _pointInLine as as, _steppedLineTo as at, _bezierCurveTo as au, drawPoint as av, addRoundedRectPath as aw, toTRBL as ax, toTRBLCorners as ay, _boundSegment as az, isArray as b, fontString as b0, toLineHeight as b1, PITAU as b2, INFINITY as b3, RAD_PER_DEG as b4, QUARTER_PI as b5, TWO_THIRDS_PI as b6, _angleDiff as b7, color as c, defaults as d, effects as e, resolveObjectKey as f, isNumberFinite as g, defined as h, isObject as i, createContext as j, isNullOrUndef as k, listenArrayEvents as l, toPercentage as m, toDimension as n, formatNumber as o, _angleBetween as p, _getStartAndCountOfVisiblePoints as q, requestAnimFrame as r, sign as s, toRadians as t, unlistenArrayEvents as u, valueOrDefault as v, _scaleRangesChanged as w, isNumber as x, _parseObjectDataRadialScale as y, getRelativePosition as z };
//# sourceMappingURL=helpers.dataset.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,65 @@
export default class BarController extends DatasetController {
static id: string;
/**
* @type {any}
*/
static overrides: any;
/**
* Overriding primitive data parsing since we support mixed primitive/array
* data for float bars
* @protected
*/
protected parsePrimitiveData(meta: any, data: any, start: any, count: any): any[];
/**
* Overriding array data parsing since we support mixed primitive/array
* data for float bars
* @protected
*/
protected parseArrayData(meta: any, data: any, start: any, count: any): any[];
/**
* Overriding object data parsing since we support mixed primitive/array
* value-scale data for float bars
* @protected
*/
protected parseObjectData(meta: any, data: any, start: any, count: any): any[];
update(mode: any): void;
/**
* Returns the stacks based on groups and bar visibility.
* @param {number} [last] - The dataset index
* @param {number} [dataIndex] - The data index of the ruler
* @returns {string[]} The list of stack IDs
* @private
*/
private _getStacks;
/**
* Returns the effective number of stacks based on groups and bar visibility.
* @private
*/
private _getStackCount;
_getAxisCount(): number;
getFirstScaleIdForIndexAxis(): string;
_getAxis(): string[];
/**
* Returns the stack index for the given dataset based on groups and bar visibility.
* @param {number} [datasetIndex] - The dataset index
* @param {string} [name] - The stack name to find
* @param {number} [dataIndex]
* @returns {number} The stack index
* @private
*/
private _getStackIndex;
/**
* @private
*/
private _getRuler;
/**
* Note: pixel values are not clamped to the scale area.
* @private
*/
private _calculateBarValuePixels;
/**
* @private
*/
private _calculateBarIndexPixels;
}
import DatasetController from "../core/core.datasetController.js";
@@ -0,0 +1,35 @@
export default class BubbleController extends DatasetController {
static id: string;
/**
* @type {any}
*/
static overrides: any;
/**
* Parse array of primitive values
* @protected
*/
protected parsePrimitiveData(meta: any, data: any, start: any, count: any): any;
/**
* Parse array of arrays
* @protected
*/
protected parseArrayData(meta: any, data: any, start: any, count: any): any;
/**
* Parse array of objects
* @protected
*/
protected parseObjectData(meta: any, data: any, start: any, count: any): any;
/**
* @protected
*/
protected getMaxOverflow(): number;
/**
* @protected
*/
protected getLabelAndValue(index: any): {
label: any;
value: string;
};
update(mode: any): void;
}
import DatasetController from "../core/core.datasetController.js";
@@ -0,0 +1,64 @@
export default class DoughnutController extends DatasetController {
static id: string;
static descriptors: {
_scriptable: (name: any) => boolean;
_indexable: (name: any) => boolean;
};
/**
* @type {any}
*/
static overrides: any;
constructor(chart: any, datasetIndex: any);
innerRadius: number;
outerRadius: number;
offsetX: number;
offsetY: number;
/**
* Override data parsing, since we are not using scales
*/
parse(start: any, count: any): void;
/**
* @private
*/
private _getRotation;
/**
* @private
*/
private _getCircumference;
/**
* Get the maximal rotation & circumference extents
* across all visible datasets.
*/
_getRotationExtents(): {
rotation: number;
circumference: number;
};
/**
* @private
*/
private _circumference;
calculateTotal(): number;
calculateCircumference(value: any): number;
getLabelAndValue(index: any): {
label: any;
value: string;
};
getMaxBorderWidth(arcs: any): number;
getMaxOffset(arcs: any): number;
/**
* Get radius length offset of the dataset in relation to the visible datasets weights. This allows determining the inner and outer radius correctly
* @private
*/
private _getRingWeightOffset;
/**
* @private
*/
private _getRingWeight;
/**
* Returns the sum of all visible data set weights.
* @private
*/
private _getVisibleDatasetWeightTotal;
}
export type Chart = import('../core/core.controller.js').default;
import DatasetController from "../core/core.datasetController.js";
@@ -0,0 +1,13 @@
export default class LineController extends DatasetController {
static id: string;
/**
* @type {any}
*/
static overrides: any;
update(mode: any): void;
/**
* @protected
*/
protected getMaxOverflow(): any;
}
import DatasetController from "../core/core.datasetController.js";
@@ -0,0 +1,3 @@
export default class PieController extends DoughnutController {
}
import DoughnutController from "./controller.doughnut.js";
@@ -0,0 +1,35 @@
export default class PolarAreaController extends DatasetController {
static id: string;
/**
* @type {any}
*/
static overrides: any;
constructor(chart: any, datasetIndex: any);
innerRadius: number;
outerRadius: number;
getLabelAndValue(index: any): {
label: any;
value: string;
};
parseObjectData(meta: any, data: any, start: any, count: any): {
r: unknown;
}[];
update(mode: any): void;
/**
* @protected
*/
protected getMinMax(): {
min: number;
max: number;
};
/**
* @private
*/
private _updateRadius;
countVisibleElements(): number;
/**
* @private
*/
private _computeAngle;
}
import DatasetController from "../core/core.datasetController.js";
@@ -0,0 +1,19 @@
export default class RadarController extends DatasetController {
static id: string;
/**
* @type {any}
*/
static overrides: any;
/**
* @protected
*/
protected getLabelAndValue(index: any): {
label: any;
value: string;
};
parseObjectData(meta: any, data: any, start: any, count: any): {
r: unknown;
}[];
update(mode: any): void;
}
import DatasetController from "../core/core.datasetController.js";
@@ -0,0 +1,20 @@
export default class ScatterController extends DatasetController {
static id: string;
/**
* @type {any}
*/
static overrides: any;
/**
* @protected
*/
protected getLabelAndValue(index: any): {
label: any;
value: string;
};
update(mode: any): void;
/**
* @protected
*/
protected getMaxOverflow(): any;
}
import DatasetController from "../core/core.datasetController.js";
+8
View File
@@ -0,0 +1,8 @@
export { default as BarController } from "./controller.bar.js";
export { default as BubbleController } from "./controller.bubble.js";
export { default as DoughnutController } from "./controller.doughnut.js";
export { default as LineController } from "./controller.line.js";
export { default as PolarAreaController } from "./controller.polarArea.js";
export { default as PieController } from "./controller.pie.js";
export { default as RadarController } from "./controller.radar.js";
export { default as ScatterController } from "./controller.scatter.js";
+67
View File
@@ -0,0 +1,67 @@
/**
* @namespace Chart._adapters
* @since 2.8.0
* @private
*/
import type { AnyObject } from '../types/basic.js';
import type { ChartOptions } from '../types/index.js';
export type TimeUnit = 'millisecond' | 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year';
export interface DateAdapter<T extends AnyObject = AnyObject> {
readonly options: T;
/**
* Will called with chart options after adapter creation.
*/
init(this: DateAdapter<T>, chartOptions: ChartOptions): void;
/**
* Returns a map of time formats for the supported formatting units defined
* in Unit as well as 'datetime' representing a detailed date/time string.
*/
formats(this: DateAdapter<T>): Record<TimeUnit | 'datetime', string>;
/**
* Parses the given `value` and return the associated timestamp.
* @param value - the value to parse (usually comes from the data)
* @param [format] - the expected data format
*/
parse(this: DateAdapter<T>, value: unknown, format?: string): number | null;
/**
* Returns the formatted date in the specified `format` for a given `timestamp`.
* @param timestamp - the timestamp to format
* @param format - the date/time token
*/
format(this: DateAdapter<T>, timestamp: number, format: string): string;
/**
* Adds the specified `amount` of `unit` to the given `timestamp`.
* @param timestamp - the input timestamp
* @param amount - the amount to add
* @param unit - the unit as string
*/
add(this: DateAdapter<T>, timestamp: number, amount: number, unit: TimeUnit): number;
/**
* Returns the number of `unit` between the given timestamps.
* @param a - the input timestamp (reference)
* @param b - the timestamp to subtract
* @param unit - the unit as string
*/
diff(this: DateAdapter<T>, a: number, b: number, unit: TimeUnit): number;
/**
* Returns start of `unit` for the given `timestamp`.
* @param timestamp - the input timestamp
* @param unit - the unit as string
* @param [weekday] - the ISO day of the week with 1 being Monday
* and 7 being Sunday (only needed if param *unit* is `isoWeek`).
*/
startOf(this: DateAdapter<T>, timestamp: number, unit: TimeUnit | 'isoWeek', weekday?: number | boolean): number;
/**
* Returns end of `unit` for the given `timestamp`.
* @param timestamp - the input timestamp
* @param unit - the unit as string
*/
endOf(this: DateAdapter<T>, timestamp: number, unit: TimeUnit): number;
}
declare const _default: {
_date: {
new (options?: AnyObject): DateAdapter;
override<T extends AnyObject = AnyObject>(members: Partial<Omit<DateAdapter<T>, "options">>): void;
};
};
export default _default;
+21
View File
@@ -0,0 +1,21 @@
export default class Animation {
constructor(cfg: any, target: any, prop: any, to: any);
_active: boolean;
_fn: any;
_easing: any;
_start: number;
_duration: number;
_total: number;
_loop: boolean;
_target: any;
_prop: any;
_from: unknown;
_to: any;
_promises: any[];
active(): boolean;
update(cfg: any, to: any, date: any): void;
cancel(): void;
tick(date: any): void;
wait(): Promise<any>;
_notify(resolved: any): void;
}
+22
View File
@@ -0,0 +1,22 @@
export default class Animations {
constructor(chart: any, config: any);
_chart: any;
_properties: Map<any, any>;
configure(config: any): void;
/**
* Utility to handle animation of `options`.
* @private
*/
private _animateOptions;
/**
* @private
*/
private _createAnimations;
/**
* Update `target` properties to new values, using configured animations
* @param {object} target - object to update
* @param {object} values - new target properties
* @returns {boolean|undefined} - `true` if animations were started
**/
update(target: object, values: object): boolean | undefined;
}
@@ -0,0 +1 @@
export function applyAnimationsDefaults(defaults: any): void;
+67
View File
@@ -0,0 +1,67 @@
/**
* @typedef { import('./core.animation.js').default } Animation
* @typedef { import('./core.controller.js').default } Chart
*/
/**
* Please use the module's default export which provides a singleton instance
* Note: class is export for typedoc
*/
export class Animator {
_request: any;
_charts: Map<any, any>;
_running: boolean;
_lastDate: number;
/**
* @private
*/
private _notify;
/**
* @private
*/
private _refresh;
/**
* @private
*/
private _update;
/**
* @private
*/
private _getAnims;
/**
* @param {Chart} chart
* @param {string} event - event name
* @param {Function} cb - callback
*/
listen(chart: Chart, event: string, cb: Function): void;
/**
* Add animations
* @param {Chart} chart
* @param {Animation[]} items - animations
*/
add(chart: Chart, items: Animation[]): void;
/**
* Counts number of active animations for the chart
* @param {Chart} chart
*/
has(chart: Chart): boolean;
/**
* Start animating (all charts)
* @param {Chart} chart
*/
start(chart: Chart): void;
running(chart: any): boolean;
/**
* Stop all animations for the chart
* @param {Chart} chart
*/
stop(chart: Chart): void;
/**
* Remove chart from Animator
* @param {Chart} chart
*/
remove(chart: Chart): boolean;
}
declare const _default: Animator;
export default _default;
export type Animation = import('./core.animation.js').default;
export type Chart = import('./core.controller.js').default;
+86
View File
@@ -0,0 +1,86 @@
export function getIndexAxis(type: any, options: any): any;
export function determineAxis(id: any, ...scaleOptions: any[]): any;
export default class Config {
constructor(config: any);
_config: any;
_scopeCache: Map<any, any>;
_resolverCache: Map<any, any>;
get platform(): any;
set type(arg: any);
get type(): any;
set data(arg: any);
get data(): any;
set options(arg: any);
get options(): any;
get plugins(): any;
update(): void;
clearCache(): void;
/**
* Returns the option scope keys for resolving dataset options.
* These keys do not include the dataset itself, because it is not under options.
* @param {string} datasetType
* @return {string[][]}
*/
datasetScopeKeys(datasetType: string): string[][];
/**
* Returns the option scope keys for resolving dataset animation options.
* These keys do not include the dataset itself, because it is not under options.
* @param {string} datasetType
* @param {string} transition
* @return {string[][]}
*/
datasetAnimationScopeKeys(datasetType: string, transition: string): string[][];
/**
* Returns the options scope keys for resolving element options that belong
* to an dataset. These keys do not include the dataset itself, because it
* is not under options.
* @param {string} datasetType
* @param {string} elementType
* @return {string[][]}
*/
datasetElementScopeKeys(datasetType: string, elementType: string): string[][];
/**
* Returns the options scope keys for resolving plugin options.
* @param {{id: string, additionalOptionScopes?: string[]}} plugin
* @return {string[][]}
*/
pluginScopeKeys(plugin: {
id: string;
additionalOptionScopes?: string[];
}): string[][];
/**
* @private
*/
private _cachedScopes;
/**
* Resolves the objects from options and defaults for option value resolution.
* @param {object} mainScope - The main scope object for options
* @param {string[][]} keyLists - The arrays of keys in resolution order
* @param {boolean} [resetCache] - reset the cache for this mainScope
*/
getOptionScopes(mainScope: object, keyLists: string[][], resetCache?: boolean): any;
/**
* Returns the option scopes for resolving chart options
* @return {object[]}
*/
chartOptionScopes(): object[];
/**
* @param {object[]} scopes
* @param {string[]} names
* @param {function|object} context
* @param {string[]} [prefixes]
* @return {object}
*/
resolveNamedOptions(scopes: object[], names: string[], context: Function | object, prefixes?: string[]): object;
/**
* @param {object[]} scopes
* @param {object} [context]
* @param {string[]} [prefixes]
* @param {{scriptable: boolean, indexable: boolean, allKeys?: boolean}} [descriptorDefaults]
*/
createResolver(scopes: object[], context?: object, prefixes?: string[], descriptorDefaults?: {
scriptable: boolean;
indexable: boolean;
allKeys?: boolean;
}): any;
}
+257
View File
@@ -0,0 +1,257 @@
export default Chart;
export type ChartEvent = import('../types/index.js').ChartEvent;
export type Point = import('../types/index.js').Point;
declare class Chart {
static defaults: import("./core.defaults.js").Defaults;
static instances: {};
static overrides: any;
static registry: import("./core.registry.js").Registry;
static version: string;
static getChart: (key: any) => any;
static register(...items: any[]): void;
static unregister(...items: any[]): void;
constructor(item: any, userConfig: any);
config: Config;
platform: any;
id: number;
ctx: any;
canvas: any;
width: any;
height: any;
_options: any;
_aspectRatio: any;
_layers: any[];
_metasets: any[];
_stacks: any;
boxes: any[];
currentDevicePixelRatio: any;
chartArea: any;
_active: any[];
_lastEvent: import("../types/index.js").ChartEvent;
_listeners: {};
/** @type {?{attach?: function, detach?: function, resize?: function}} */
_responsiveListeners: {
attach?: Function;
detach?: Function;
resize?: Function;
};
_sortedMetasets: any[];
scales: {};
_plugins: PluginService;
$proxies: {};
_hiddenIndices: {};
attached: boolean;
_animationsDisabled: boolean;
$context: {
chart: Chart;
type: string;
};
_doResize: (mode?: any) => number;
_dataChanges: any[];
get aspectRatio(): any;
set data(arg: any);
get data(): any;
set options(arg: any);
get options(): any;
get registry(): import("./core.registry.js").Registry;
/**
* @private
*/
private _initialize;
clear(): Chart;
stop(): Chart;
/**
* Resize the chart to its container or to explicit dimensions.
* @param {number} [width]
* @param {number} [height]
*/
resize(width?: number, height?: number): void;
_resizeBeforeDraw: {
width: number;
height: number;
};
_resize(width: any, height: any): void;
ensureScalesHaveIDs(): void;
/**
* Builds a map of scale ID to scale object for future lookup.
*/
buildOrUpdateScales(): void;
/**
* @private
*/
private _updateMetasets;
/**
* @private
*/
private _removeUnreferencedMetasets;
buildOrUpdateControllers(): any[];
/**
* Reset the elements of all datasets
* @private
*/
private _resetElements;
/**
* Resets the chart back to its state before the initial animation
*/
reset(): void;
update(mode: any): void;
_minPadding: number;
/**
* @private
*/
private _updateScales;
/**
* @private
*/
private _checkEventBindings;
/**
* @private
*/
private _updateHiddenIndices;
/**
* @private
*/
private _getUniformDataChanges;
/**
* Updates the chart layout unless a plugin returns `false` to the `beforeLayout`
* hook, in which case, plugins will not be called on `afterLayout`.
* @private
*/
private _updateLayout;
/**
* Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate`
* hook, in which case, plugins will not be called on `afterDatasetsUpdate`.
* @private
*/
private _updateDatasets;
/**
* Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate`
* hook, in which case, plugins will not be called on `afterDatasetUpdate`.
* @private
*/
private _updateDataset;
render(): void;
draw(): void;
/**
* @private
*/
private _getSortedDatasetMetas;
/**
* Gets the visible dataset metas in drawing order
* @return {object[]}
*/
getSortedVisibleDatasetMetas(): object[];
/**
* Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw`
* hook, in which case, plugins will not be called on `afterDatasetsDraw`.
* @private
*/
private _drawDatasets;
/**
* Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw`
* hook, in which case, plugins will not be called on `afterDatasetDraw`.
* @private
*/
private _drawDataset;
/**
* Checks whether the given point is in the chart area.
* @param {Point} point - in relative coordinates (see, e.g., getRelativePosition)
* @returns {boolean}
*/
isPointInArea(point: Point): boolean;
getElementsAtEventForMode(e: any, mode: any, options: any, useFinalPosition: any): any;
getDatasetMeta(datasetIndex: any): any;
getContext(): {
chart: Chart;
type: string;
};
getVisibleDatasetCount(): number;
isDatasetVisible(datasetIndex: any): boolean;
setDatasetVisibility(datasetIndex: any, visible: any): void;
toggleDataVisibility(index: any): void;
getDataVisibility(index: any): boolean;
/**
* @private
*/
private _updateVisibility;
hide(datasetIndex: any, dataIndex: any): void;
show(datasetIndex: any, dataIndex: any): void;
/**
* @private
*/
private _destroyDatasetMeta;
_stop(): void;
destroy(): void;
toBase64Image(...args: any[]): any;
/**
* @private
*/
private bindEvents;
/**
* @private
*/
private bindUserEvents;
/**
* @private
*/
private bindResponsiveEvents;
/**
* @private
*/
private unbindEvents;
updateHoverStyle(items: any, mode: any, enabled: any): void;
/**
* Get active (hovered) elements
* @returns array
*/
getActiveElements(): any[];
/**
* Set active (hovered) elements
* @param {array} activeElements New active data points
*/
setActiveElements(activeElements: any[]): void;
/**
* Calls enabled plugins on the specified hook and with the given args.
* This method immediately returns as soon as a plugin explicitly returns false. The
* returned value can be used, for instance, to interrupt the current action.
* @param {string} hook - The name of the plugin method to call (e.g. 'beforeUpdate').
* @param {Object} [args] - Extra arguments to apply to the hook call.
* @param {import('./core.plugins.js').filterCallback} [filter] - Filtering function for limiting which plugins are notified
* @returns {boolean} false if any of the plugins return false, else returns true.
*/
notifyPlugins(hook: string, args?: any, filter?: import('./core.plugins.js').filterCallback): boolean;
/**
* Check if a plugin with the specific ID is registered and enabled
* @param {string} pluginId - The ID of the plugin of which to check if it is enabled
* @returns {boolean}
*/
isPluginEnabled(pluginId: string): boolean;
/**
* @private
*/
private _updateHoverStyles;
/**
* @private
*/
private _eventHandler;
/**
* Handle an event
* @param {ChartEvent} e the event to handle
* @param {boolean} [replay] - true if the event was replayed by `update`
* @param {boolean} [inChartArea] - true if the event is inside chartArea
* @return {boolean} true if the chart needs to re-render
* @private
*/
private _handleEvent;
/**
* @param {ChartEvent} e - The event
* @param {import('../types/index.js').ActiveElement[]} lastActive - Previously active elements
* @param {boolean} inChartArea - Is the event inside chartArea
* @param {boolean} useFinalPosition - Should the evaluation be done with current or final (after animation) element positions
* @returns {import('../types/index.js').ActiveElement[]} - The active elements
* @pravate
*/
_getActiveElements(e: ChartEvent, lastActive: import('../types/index.js').ActiveElement[], inChartArea: boolean, useFinalPosition: boolean): import('../types/index.js').ActiveElement[];
}
import Config from "./core.config.js";
import PluginService from "./core.plugins.js";
@@ -0,0 +1,251 @@
export default class DatasetController {
/**
* @type {any}
*/
static defaults: any;
/**
* Element type used to generate a meta dataset (e.g. Chart.element.LineElement).
*/
static datasetElementType: any;
/**
* Element type used to generate a meta data (e.g. Chart.element.PointElement).
*/
static dataElementType: any;
/**
* @param {Chart} chart
* @param {number} datasetIndex
*/
constructor(chart: Chart, datasetIndex: number);
chart: import("./core.controller.js").default;
_ctx: any;
index: number;
_cachedDataOpts: {};
_cachedMeta: any;
_type: any;
options: any;
/** @type {boolean | object} */
_parsing: boolean | object;
_data: any;
_objectData: any;
_sharedOptions: any;
_drawStart: any;
_drawCount: any;
enableOptionSharing: boolean;
supportsDecimation: boolean;
$context: any;
_syncList: any[];
datasetElementType: any;
dataElementType: any;
initialize(): void;
updateIndex(datasetIndex: any): void;
linkScales(): void;
getDataset(): any;
getMeta(): any;
/**
* @param {string} scaleID
* @return {Scale}
*/
getScaleForId(scaleID: string): Scale;
/**
* @private
*/
private _getOtherScale;
reset(): void;
/**
* @private
*/
private _destroy;
/**
* @private
*/
private _dataCheck;
addElements(): void;
buildOrUpdateElements(resetNewElements: any): void;
/**
* Merges user-supplied and default dataset-level options
* @private
*/
private configure;
/**
* @param {number} start
* @param {number} count
*/
parse(start: number, count: number): void;
/**
* Parse array of primitive values
* @param {object} meta - dataset meta
* @param {array} data - data array. Example [1,3,4]
* @param {number} start - start index
* @param {number} count - number of items to parse
* @returns {object} parsed item - item containing index and a parsed value
* for each scale id.
* Example: {xScale0: 0, yScale0: 1}
* @protected
*/
protected parsePrimitiveData(meta: object, data: any[], start: number, count: number): object;
/**
* Parse array of arrays
* @param {object} meta - dataset meta
* @param {array} data - data array. Example [[1,2],[3,4]]
* @param {number} start - start index
* @param {number} count - number of items to parse
* @returns {object} parsed item - item containing index and a parsed value
* for each scale id.
* Example: {x: 0, y: 1}
* @protected
*/
protected parseArrayData(meta: object, data: any[], start: number, count: number): object;
/**
* Parse array of objects
* @param {object} meta - dataset meta
* @param {array} data - data array. Example [{x:1, y:5}, {x:2, y:10}]
* @param {number} start - start index
* @param {number} count - number of items to parse
* @returns {object} parsed item - item containing index and a parsed value
* for each scale id. _custom is optional
* Example: {xScale0: 0, yScale0: 1, _custom: {r: 10, foo: 'bar'}}
* @protected
*/
protected parseObjectData(meta: object, data: any[], start: number, count: number): object;
/**
* @protected
*/
protected getParsed(index: any): any;
/**
* @protected
*/
protected getDataElement(index: any): any;
/**
* @protected
*/
protected applyStack(scale: any, parsed: any, mode: any): any;
/**
* @protected
*/
protected updateRangeFromParsed(range: any, scale: any, parsed: any, stack: any): void;
/**
* @protected
*/
protected getMinMax(scale: any, canStack: any): {
min: number;
max: number;
};
getAllParsedValues(scale: any): number[];
/**
* @return {number|boolean}
* @protected
*/
protected getMaxOverflow(): number | boolean;
/**
* @protected
*/
protected getLabelAndValue(index: any): {
label: string;
value: string;
};
/**
* @private
*/
private _update;
/**
* @param {string} mode
*/
update(mode: string): void;
draw(): void;
/**
* Returns a set of predefined style properties that should be used to represent the dataset
* or the data if the index is specified
* @param {number} index - data index
* @param {boolean} [active] - true if hover
* @return {object} style object
*/
getStyle(index: number, active?: boolean): object;
/**
* @protected
*/
protected getContext(index: any, active: any, mode: any): any;
/**
* @param {string} [mode]
* @protected
*/
protected resolveDatasetElementOptions(mode?: string): any;
/**
* @param {number} index
* @param {string} [mode]
* @protected
*/
protected resolveDataElementOptions(index: number, mode?: string): any;
/**
* @private
*/
private _resolveElementOptions;
/**
* @private
*/
private _resolveAnimations;
/**
* Utility for getting the options object shared between elements
* @protected
*/
protected getSharedOptions(options: any): any;
/**
* Utility for determining if `options` should be included in the updated properties
* @protected
*/
protected includeOptions(mode: any, sharedOptions: any): boolean;
/**
* @todo v4, rename to getSharedOptions and remove excess functions
*/
_getSharedOptions(start: any, mode: any): {
sharedOptions: any;
includeOptions: boolean;
};
/**
* Utility for updating an element with new properties, using animations when appropriate.
* @protected
*/
protected updateElement(element: any, index: any, properties: any, mode: any): void;
/**
* Utility to animate the shared options, that are potentially affecting multiple elements.
* @protected
*/
protected updateSharedOptions(sharedOptions: any, mode: any, newOptions: any): void;
/**
* @private
*/
private _setStyle;
removeHoverStyle(element: any, datasetIndex: any, index: any): void;
setHoverStyle(element: any, datasetIndex: any, index: any): void;
/**
* @private
*/
private _removeDatasetHoverStyle;
/**
* @private
*/
private _setDatasetHoverStyle;
/**
* @private
*/
private _resyncElements;
/**
* @private
*/
private _insertElements;
updateElements(element: any, start: any, count: any, mode: any): void;
/**
* @private
*/
private _removeElements;
/**
* @private
*/
private _sync;
_onDataPush(...args: any[]): void;
_onDataPop(): void;
_onDataShift(): void;
_onDataSplice(start: any, count: any, ...args: any[]): void;
_onDataUnshift(...args: any[]): void;
}
export type Chart = import('./core.controller.js').default;
export type Scale = import('./core.scale.js').default;
+80
View File
@@ -0,0 +1,80 @@
export const overrides: any;
export const descriptors: any;
/**
* Please use the module's default export which provides a singleton instance
* Note: class is exported for typedoc
*/
export class Defaults {
constructor(_descriptors: any, _appliers: any);
animation: any;
backgroundColor: string;
borderColor: string;
color: string;
datasets: {};
devicePixelRatio: (context: any) => any;
elements: {};
events: string[];
font: {
family: string;
size: number;
style: string;
lineHeight: number;
weight: any;
};
hover: {};
hoverBackgroundColor: (ctx: any, options: any) => CanvasGradient;
hoverBorderColor: (ctx: any, options: any) => CanvasGradient;
hoverColor: (ctx: any, options: any) => CanvasGradient;
indexAxis: string;
interaction: {
mode: string;
intersect: boolean;
includeInvisible: boolean;
};
maintainAspectRatio: boolean;
onHover: any;
onClick: any;
parsing: boolean;
plugins: {};
responsive: boolean;
scale: any;
scales: {};
showLine: boolean;
drawActiveElementsOnTop: boolean;
/**
* @param {string|object} scope
* @param {object} [values]
*/
set(scope: string | object, values?: object): any;
/**
* @param {string} scope
*/
get(scope: string): any;
/**
* @param {string|object} scope
* @param {object} [values]
*/
describe(scope: string | object, values?: object): any;
override(scope: any, values: any): any;
/**
* Routes the named defaults to fallback to another scope/name.
* This routing is useful when those target values, like defaults.color, are changed runtime.
* If the values would be copied, the runtime change would not take effect. By routing, the
* fallback is evaluated at each access, so its always up to date.
*
* Example:
*
* defaults.route('elements.arc', 'backgroundColor', '', 'color')
* - reads the backgroundColor from defaults.color when undefined locally
*
* @param {string} scope Scope this route applies to.
* @param {string} name Property name that should be routed to different namespace when not defined here.
* @param {string} targetScope The namespace where those properties should be routed to.
* Empty string ('') is the root of defaults.
* @param {string} targetName The target name in the target scope the property should be routed to.
*/
route(scope: string, name: string, targetScope: string, targetName: string): void;
apply(appliers: any): void;
}
declare const _default: Defaults;
export default _default;
+21
View File
@@ -0,0 +1,21 @@
import type { AnyObject } from '../types/basic.js';
import type { Point } from '../types/geometric.js';
import type { Animation } from '../types/animation.js';
export default class Element<T = AnyObject, O = AnyObject> {
static defaults: {};
static defaultRoutes: any;
x: number;
y: number;
active: boolean;
options: O;
$animations: Record<keyof T, Animation>;
tooltipPosition(useFinalPosition: boolean): Point;
hasValue(): boolean;
/**
* Gets the current or final value of each prop. Can return extra properties (whole object).
* @param props - properties to get
* @param [final] - get the final value (animation target)
*/
getProps<P extends (keyof T)[]>(props: P, final?: boolean): Pick<T, P[number]>;
getProps<P extends string>(props: P[], final?: boolean): Partial<Record<P, unknown>>;
}
+92
View File
@@ -0,0 +1,92 @@
declare namespace _default {
export { evaluateInteractionItems };
export namespace modes {
/**
* Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something
* If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item
* @function Chart.Interaction.modes.index
* @since v2.4.0
* @param {Chart} chart - the chart we are returning items from
* @param {Event} e - the event we are find things at
* @param {InteractionOptions} options - options to use
* @param {boolean} [useFinalPosition] - use final element position (animation target)
* @return {InteractionItem[]} - items that are found
*/
function index(chart: import("./core.controller.js").default, e: Event, options: InteractionOptions, useFinalPosition?: boolean): InteractionItem[];
/**
* Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something
* If the options.intersect is false, we find the nearest item and return the items in that dataset
* @function Chart.Interaction.modes.dataset
* @param {Chart} chart - the chart we are returning items from
* @param {Event} e - the event we are find things at
* @param {InteractionOptions} options - options to use
* @param {boolean} [useFinalPosition] - use final element position (animation target)
* @return {InteractionItem[]} - items that are found
*/
function dataset(chart: import("./core.controller.js").default, e: Event, options: InteractionOptions, useFinalPosition?: boolean): InteractionItem[];
/**
* Point mode returns all elements that hit test based on the event position
* of the event
* @function Chart.Interaction.modes.intersect
* @param {Chart} chart - the chart we are returning items from
* @param {Event} e - the event we are find things at
* @param {InteractionOptions} options - options to use
* @param {boolean} [useFinalPosition] - use final element position (animation target)
* @return {InteractionItem[]} - items that are found
*/
function point(chart: import("./core.controller.js").default, e: Event, options: InteractionOptions, useFinalPosition?: boolean): InteractionItem[];
/**
* nearest mode returns the element closest to the point
* @function Chart.Interaction.modes.intersect
* @param {Chart} chart - the chart we are returning items from
* @param {Event} e - the event we are find things at
* @param {InteractionOptions} options - options to use
* @param {boolean} [useFinalPosition] - use final element position (animation target)
* @return {InteractionItem[]} - items that are found
*/
function nearest(chart: import("./core.controller.js").default, e: Event, options: InteractionOptions, useFinalPosition?: boolean): InteractionItem[];
/**
* x mode returns the elements that hit-test at the current x coordinate
* @function Chart.Interaction.modes.x
* @param {Chart} chart - the chart we are returning items from
* @param {Event} e - the event we are find things at
* @param {InteractionOptions} options - options to use
* @param {boolean} [useFinalPosition] - use final element position (animation target)
* @return {InteractionItem[]} - items that are found
*/
function x(chart: import("./core.controller.js").default, e: Event, options: InteractionOptions, useFinalPosition?: boolean): InteractionItem[];
/**
* y mode returns the elements that hit-test at the current y coordinate
* @function Chart.Interaction.modes.y
* @param {Chart} chart - the chart we are returning items from
* @param {Event} e - the event we are find things at
* @param {InteractionOptions} options - options to use
* @param {boolean} [useFinalPosition] - use final element position (animation target)
* @return {InteractionItem[]} - items that are found
*/
function y(chart: import("./core.controller.js").default, e: Event, options: InteractionOptions, useFinalPosition?: boolean): InteractionItem[];
}
}
export default _default;
export type Chart = import('./core.controller.js').default;
export type ChartEvent = import('../types/index.js').ChartEvent;
export type InteractionOptions = {
axis?: string;
intersect?: boolean;
includeInvisible?: boolean;
};
export type InteractionItem = {
datasetIndex: number;
index: number;
element: import('./core.element.js').default;
};
export type Point = import('../types/index.js').Point;
/**
* Helper function to select candidate elements for interaction
* @param {Chart} chart - the chart
* @param {string} axis - the axis mode. x|y|xy|r
* @param {Point} position - the point to be nearest to, in relative coordinates
* @param {function} handler - the callback to execute for each visible item
* @param {boolean} [intersect] - consider intersecting items
*/
declare function evaluateInteractionItems(chart: Chart, axis: string, position: Point, handler: Function, intersect?: boolean): void;
+88
View File
@@ -0,0 +1,88 @@
declare namespace _default {
/**
* Register a box to a chart.
* A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title.
* @param {Chart} chart - the chart to use
* @param {LayoutItem} item - the item to add to be laid out
*/
function addBox(chart: import("./core.controller.js").default, item: LayoutItem): void;
/**
* Remove a layoutItem from a chart
* @param {Chart} chart - the chart to remove the box from
* @param {LayoutItem} layoutItem - the item to remove from the layout
*/
function removeBox(chart: import("./core.controller.js").default, layoutItem: LayoutItem): void;
/**
* Sets (or updates) options on the given `item`.
* @param {Chart} chart - the chart in which the item lives (or will be added to)
* @param {LayoutItem} item - the item to configure with the given options
* @param {object} options - the new item options.
*/
function configure(chart: import("./core.controller.js").default, item: LayoutItem, options: any): void;
/**
* Fits boxes of the given chart into the given size by having each box measure itself
* then running a fitting algorithm
* @param {Chart} chart - the chart
* @param {number} width - the width to fit into
* @param {number} height - the height to fit into
* @param {number} minPadding - minimum padding required for each side of chart area
*/
function update(chart: import("./core.controller.js").default, width: number, height: number, minPadding: number): void;
}
export default _default;
export type Chart = import('./core.controller.js').default;
export type LayoutItem = {
/**
* - The position of the item in the chart layout. Possible values are
* 'left', 'top', 'right', 'bottom', and 'chartArea'
*/
position: string;
/**
* - The weight used to sort the item. Higher weights are further away from the chart area
*/
weight: number;
/**
* - if true, and the item is horizontal, then push vertical boxes down
*/
fullSize: boolean;
/**
* - returns true if the layout item is horizontal (ie. top or bottom)
*/
isHorizontal: Function;
/**
* - Takes two parameters: width and height. Returns size of item
*/
update: Function;
/**
* - Draws the element
*/
draw: Function;
/**
* - Returns an object with padding on the edges
*/
getPadding?: Function;
/**
* - Width of item. Must be valid after update()
*/
width: number;
/**
* - Height of item. Must be valid after update()
*/
height: number;
/**
* - Left edge of the item. Set by layout system and cannot be used in update
*/
left: number;
/**
* - Top edge of the item. Set by layout system and cannot be used in update
*/
top: number;
/**
* - Right edge of the item. Set by layout system and cannot be used in update
*/
right: number;
/**
* - Bottom edge of the item. Set by layout system and cannot be used in update
*/
bottom: number;
};
@@ -0,0 +1 @@
export function applyLayoutsDefaults(defaults: any): void;
+64
View File
@@ -0,0 +1,64 @@
/**
* @typedef { import('./core.controller.js').default } Chart
* @typedef { import('../types/index.js').ChartEvent } ChartEvent
* @typedef { import('../plugins/plugin.tooltip.js').default } Tooltip
*/
/**
* @callback filterCallback
* @param {{plugin: object, options: object}} value
* @param {number} [index]
* @param {array} [array]
* @param {object} [thisArg]
* @return {boolean}
*/
export default class PluginService {
_init: {
plugin: any;
options: any;
}[];
/**
* Calls enabled plugins for `chart` on the specified hook and with the given args.
* This method immediately returns as soon as a plugin explicitly returns false. The
* returned value can be used, for instance, to interrupt the current action.
* @param {Chart} chart - The chart instance for which plugins should be called.
* @param {string} hook - The name of the plugin method to call (e.g. 'beforeUpdate').
* @param {object} [args] - Extra arguments to apply to the hook call.
* @param {filterCallback} [filter] - Filtering function for limiting which plugins are notified
* @returns {boolean} false if any of the plugins return false, else returns true.
*/
notify(chart: Chart, hook: string, args?: object, filter?: filterCallback): boolean;
/**
* @private
*/
private _notify;
invalidate(): void;
_oldCache: {
plugin: any;
options: any;
}[];
_cache: {
plugin: any;
options: any;
}[];
/**
* @param {Chart} chart
* @private
*/
private _descriptors;
_createDescriptors(chart: any, all: any): {
plugin: any;
options: any;
}[];
/**
* @param {Chart} chart
* @private
*/
private _notifyStateChanges;
}
export type Chart = import('./core.controller.js').default;
export type ChartEvent = import('../types/index.js').ChartEvent;
export type Tooltip = any;
export type filterCallback = (value: {
plugin: object;
options: object;
}, index?: number, array?: any[], thisArg?: object) => boolean;
+90
View File
@@ -0,0 +1,90 @@
/**
* Please use the module's default export which provides a singleton instance
* Note: class is exported for typedoc
*/
export class Registry {
controllers: TypedRegistry;
elements: TypedRegistry;
plugins: TypedRegistry;
scales: TypedRegistry;
_typedRegistries: TypedRegistry[];
/**
* @param {...any} args
*/
add(...args: any[]): void;
remove(...args: any[]): void;
/**
* @param {...typeof DatasetController} args
*/
addControllers(...args: (typeof DatasetController)[]): void;
/**
* @param {...typeof Element} args
*/
addElements(...args: (typeof Element)[]): void;
/**
* @param {...any} args
*/
addPlugins(...args: any[]): void;
/**
* @param {...typeof Scale} args
*/
addScales(...args: (typeof Scale)[]): void;
/**
* @param {string} id
* @returns {typeof DatasetController}
*/
getController(id: string): typeof DatasetController;
/**
* @param {string} id
* @returns {typeof Element}
*/
getElement(id: string): typeof Element;
/**
* @param {string} id
* @returns {object}
*/
getPlugin(id: string): object;
/**
* @param {string} id
* @returns {typeof Scale}
*/
getScale(id: string): typeof Scale;
/**
* @param {...typeof DatasetController} args
*/
removeControllers(...args: (typeof DatasetController)[]): void;
/**
* @param {...typeof Element} args
*/
removeElements(...args: (typeof Element)[]): void;
/**
* @param {...any} args
*/
removePlugins(...args: any[]): void;
/**
* @param {...typeof Scale} args
*/
removeScales(...args: (typeof Scale)[]): void;
/**
* @private
*/
private _each;
/**
* @private
*/
private _exec;
/**
* @private
*/
private _getRegistryForType;
/**
* @private
*/
private _get;
}
declare const _default: Registry;
export default _default;
import TypedRegistry from "./core.typedRegistry.js";
import DatasetController from "./core.datasetController.js";
import Element from "./core.element.js";
import Scale from "./core.scale.js";
@@ -0,0 +1,19 @@
/**
* @typedef { import('./core.controller.js').default } Chart
* @typedef {{value:number | string, label?:string, major?:boolean, $context?:any}} Tick
*/
/**
* Returns a subset of ticks to be plotted to avoid overlapping labels.
* @param {import('./core.scale.js').default} scale
* @param {Tick[]} ticks
* @return {Tick[]}
* @private
*/
export function autoSkip(scale: import('./core.scale.js').default, ticks: Tick[]): Tick[];
export type Chart = import('./core.controller.js').default;
export type Tick = {
value: number | string;
label?: string;
major?: boolean;
$context?: any;
};
+343
View File
@@ -0,0 +1,343 @@
export default class Scale extends Element<import("../types/basic.js").AnyObject, import("../types/basic.js").AnyObject> {
constructor(cfg: any);
/** @type {string} */
id: string;
/** @type {string} */
type: string;
/** @type {any} */
options: any;
/** @type {CanvasRenderingContext2D} */
ctx: CanvasRenderingContext2D;
/** @type {Chart} */
chart: Chart;
/** @type {number} */
top: number;
/** @type {number} */
bottom: number;
/** @type {number} */
left: number;
/** @type {number} */
right: number;
/** @type {number} */
width: number;
/** @type {number} */
height: number;
_margins: {
left: number;
right: number;
top: number;
bottom: number;
};
/** @type {number} */
maxWidth: number;
/** @type {number} */
maxHeight: number;
/** @type {number} */
paddingTop: number;
/** @type {number} */
paddingBottom: number;
/** @type {number} */
paddingLeft: number;
/** @type {number} */
paddingRight: number;
/** @type {string=} */
axis: string | undefined;
/** @type {number=} */
labelRotation: number | undefined;
min: any;
max: any;
_range: {
min: number;
max: number;
};
/** @type {Tick[]} */
ticks: Tick[];
/** @type {object[]|null} */
_gridLineItems: object[] | null;
/** @type {object[]|null} */
_labelItems: object[] | null;
/** @type {object|null} */
_labelSizes: object | null;
_length: number;
_maxLength: number;
_longestTextCache: {};
/** @type {number} */
_startPixel: number;
/** @type {number} */
_endPixel: number;
_reversePixels: boolean;
_userMax: any;
_userMin: any;
_suggestedMax: any;
_suggestedMin: any;
_ticksLength: number;
_borderValue: number;
_cache: {};
_dataLimitsCached: boolean;
$context: any;
/**
* @param {any} options
* @since 3.0
*/
init(options: any): void;
/**
* Parse a supported input value to internal representation.
* @param {*} raw
* @param {number} [index]
* @since 3.0
*/
parse(raw: any, index?: number): any;
/**
* @return {{min: number, max: number, minDefined: boolean, maxDefined: boolean}}
* @protected
* @since 3.0
*/
protected getUserBounds(): {
min: number;
max: number;
minDefined: boolean;
maxDefined: boolean;
};
/**
* @param {boolean} canStack
* @return {{min: number, max: number}}
* @protected
* @since 3.0
*/
protected getMinMax(canStack: boolean): {
min: number;
max: number;
};
/**
* Get the padding needed for the scale
* @return {{top: number, left: number, bottom: number, right: number}} the necessary padding
* @private
*/
private getPadding;
/**
* Returns the scale tick objects
* @return {Tick[]}
* @since 2.7
*/
getTicks(): Tick[];
/**
* @return {string[]}
*/
getLabels(): string[];
/**
* @return {import('../types.js').LabelItem[]}
*/
getLabelItems(chartArea?: import("../types.js").ChartArea): import('../types.js').LabelItem[];
beforeLayout(): void;
beforeUpdate(): void;
/**
* @param {number} maxWidth - the max width in pixels
* @param {number} maxHeight - the max height in pixels
* @param {{top: number, left: number, bottom: number, right: number}} margins - the space between the edge of the other scales and edge of the chart
* This space comes from two sources:
* - padding - space that's required to show the labels at the edges of the scale
* - thickness of scales or legends in another orientation
*/
update(maxWidth: number, maxHeight: number, margins: {
top: number;
left: number;
bottom: number;
right: number;
}): void;
/**
* @protected
*/
protected configure(): void;
_alignToPixels: any;
afterUpdate(): void;
beforeSetDimensions(): void;
setDimensions(): void;
afterSetDimensions(): void;
_callHooks(name: any): void;
beforeDataLimits(): void;
determineDataLimits(): void;
afterDataLimits(): void;
beforeBuildTicks(): void;
/**
* @return {object[]} the ticks
*/
buildTicks(): object[];
afterBuildTicks(): void;
beforeTickToLabelConversion(): void;
/**
* Convert ticks to label strings
* @param {Tick[]} ticks
*/
generateTickLabels(ticks: Tick[]): void;
afterTickToLabelConversion(): void;
beforeCalculateLabelRotation(): void;
calculateLabelRotation(): void;
afterCalculateLabelRotation(): void;
afterAutoSkip(): void;
beforeFit(): void;
fit(): void;
_calculatePadding(first: any, last: any, sin: any, cos: any): void;
/**
* Handle margins and padding interactions
* @private
*/
private _handleMargins;
afterFit(): void;
/**
* @return {boolean}
*/
isHorizontal(): boolean;
/**
* @return {boolean}
*/
isFullSize(): boolean;
/**
* @param {Tick[]} ticks
* @private
*/
private _convertTicksToLabels;
/**
* @return {{ first: object, last: object, widest: object, highest: object, widths: Array, heights: array }}
* @private
*/
private _getLabelSizes;
/**
* Returns {width, height, offset} objects for the first, last, widest, highest tick
* labels where offset indicates the anchor point offset from the top in pixels.
* @return {{ first: object, last: object, widest: object, highest: object, widths: Array, heights: array }}
* @private
*/
private _computeLabelSizes;
/**
* Used to get the label to display in the tooltip for the given value
* @param {*} value
* @return {string}
*/
getLabelForValue(value: any): string;
/**
* Returns the location of the given data point. Value can either be an index or a numerical value
* The coordinate (0, 0) is at the upper-left corner of the canvas
* @param {*} value
* @param {number} [index]
* @return {number}
*/
getPixelForValue(value: any, index?: number): number;
/**
* Used to get the data value from a given pixel. This is the inverse of getPixelForValue
* The coordinate (0, 0) is at the upper-left corner of the canvas
* @param {number} pixel
* @return {*}
*/
getValueForPixel(pixel: number): any;
/**
* Returns the location of the tick at the given index
* The coordinate (0, 0) is at the upper-left corner of the canvas
* @param {number} index
* @return {number}
*/
getPixelForTick(index: number): number;
/**
* Utility for getting the pixel location of a percentage of scale
* The coordinate (0, 0) is at the upper-left corner of the canvas
* @param {number} decimal
* @return {number}
*/
getPixelForDecimal(decimal: number): number;
/**
* @param {number} pixel
* @return {number}
*/
getDecimalForPixel(pixel: number): number;
/**
* Returns the pixel for the minimum chart value
* The coordinate (0, 0) is at the upper-left corner of the canvas
* @return {number}
*/
getBasePixel(): number;
/**
* @return {number}
*/
getBaseValue(): number;
/**
* @protected
*/
protected getContext(index: any): any;
/**
* @return {number}
* @private
*/
private _tickSize;
/**
* @return {boolean}
* @private
*/
private _isVisible;
/**
* @private
*/
private _computeGridLineItems;
/**
* @private
*/
private _computeLabelItems;
_getXAxisLabelAlignment(): string;
_getYAxisLabelAlignment(tl: any): {
textAlign: string;
x: any;
};
/**
* @private
*/
private _computeLabelArea;
/**
* @protected
*/
protected drawBackground(): void;
getLineWidthForValue(value: any): any;
/**
* @protected
*/
protected drawGrid(chartArea: any): void;
/**
* @protected
*/
protected drawBorder(): void;
/**
* @protected
*/
protected drawLabels(chartArea: any): void;
/**
* @protected
*/
protected drawTitle(): void;
draw(chartArea: any): void;
/**
* @return {object[]}
* @private
*/
private _layers;
/**
* Returns visible dataset metas that are attached to this scale
* @param {string} [type] - if specified, also filter by dataset type
* @return {object[]}
*/
getMatchingVisibleMetas(type?: string): object[];
/**
* @param {number} index
* @return {object}
* @protected
*/
protected _resolveTickFontOptions(index: number): object;
/**
* @protected
*/
protected _maxDigits(): number;
}
export type Chart = import('../types/index.js').Chart;
export type Tick = {
value: number | string;
label?: string;
major?: boolean;
$context?: any;
};
import Element from "./core.element.js";
@@ -0,0 +1 @@
export function applyScaleDefaults(defaults: any): void;
+31
View File
@@ -0,0 +1,31 @@
declare namespace _default {
export { formatters };
}
export default _default;
declare namespace formatters {
/**
* Formatter for value labels
* @method Chart.Ticks.formatters.values
* @param value the value to display
* @return {string|string[]} the label to display
*/
function values(value: any): string | string[];
/**
* Formatter for numeric ticks
* @method Chart.Ticks.formatters.numeric
* @param tickValue {number} the value to be formatted
* @param index {number} the position of the tickValue parameter in the ticks array
* @param ticks {object[]} the list of ticks being converted
* @return {string} string representation of the tickValue parameter
*/
function numeric(tickValue: number, index: number, ticks: any[]): string;
/**
* Formatter for logarithmic ticks
* @method Chart.Ticks.formatters.logarithmic
* @param tickValue {number} the value to be formatted
* @param index {number} the position of the tickValue parameter in the ticks array
* @param ticks {object[]} the list of ticks being converted
* @return {string} string representation of the tickValue parameter
*/
function logarithmic(tickValue: number, index: number, ticks: any[]): string;
}
@@ -0,0 +1,33 @@
/**
* @typedef {{id: string, defaults: any, overrides?: any, defaultRoutes: any}} IChartComponent
*/
export default class TypedRegistry {
constructor(type: any, scope: any, override: any);
type: any;
scope: any;
override: any;
items: any;
isForType(type: any): boolean;
/**
* @param {IChartComponent} item
* @returns {string} The scope where items defaults were registered to.
*/
register(item: IChartComponent): string;
/**
* @param {string} id
* @returns {object?}
*/
get(id: string): object | null;
/**
* @param {IChartComponent} item
*/
unregister(item: IChartComponent): void;
}
export type IChartComponent = {
id: string;
defaults: any;
overrides?: any;
defaultRoutes: any;
};
import defaults from "./core.defaults.js";
import { overrides } from "./core.defaults.js";
+15
View File
@@ -0,0 +1,15 @@
export type { DateAdapter, TimeUnit } from './core.adapters.js';
export { default as _adapters } from './core.adapters.js';
export { default as Animation } from './core.animation.js';
export { default as Animations } from './core.animations.js';
export { default as animator } from './core.animator.js';
export { default as Chart } from './core.controller.js';
export { default as DatasetController } from './core.datasetController.js';
export { default as defaults } from './core.defaults.js';
export { default as Element } from './core.element.js';
export { default as Interaction } from './core.interaction.js';
export { default as layouts } from './core.layouts.js';
export { default as plugins } from './core.plugins.js';
export { default as registry } from './core.registry.js';
export { default as Scale } from './core.scale.js';
export { default as Ticks } from './core.ticks.js';
+51
View File
@@ -0,0 +1,51 @@
import Element from '../core/core.element.js';
import type { ArcOptions, Point } from '../types/index.js';
export interface ArcProps extends Point {
startAngle: number;
endAngle: number;
innerRadius: number;
outerRadius: number;
circumference: number;
}
export default class ArcElement extends Element<ArcProps, ArcOptions> {
static id: string;
static defaults: {
borderAlign: string;
borderColor: string;
borderDash: any[];
borderDashOffset: number;
borderJoinStyle: any;
borderRadius: number;
borderWidth: number;
offset: number;
spacing: number;
angle: any;
circular: boolean;
selfJoin: boolean;
};
static defaultRoutes: {
backgroundColor: string;
};
static descriptors: {
_scriptable: boolean;
_indexable: (name: any) => boolean;
};
circumference: number;
endAngle: number;
fullCircles: number;
innerRadius: number;
outerRadius: number;
pixelMargin: number;
startAngle: number;
constructor(cfg: any);
inRange(chartX: number, chartY: number, useFinalPosition: boolean): boolean;
getCenterPoint(useFinalPosition: boolean): {
x: number;
y: number;
};
tooltipPosition(useFinalPosition: boolean): {
x: number;
y: number;
};
draw(ctx: CanvasRenderingContext2D): void;
}
+32
View File
@@ -0,0 +1,32 @@
export default class BarElement extends Element<import("../types/basic.js").AnyObject, import("../types/basic.js").AnyObject> {
static id: string;
/**
* @type {any}
*/
static defaults: any;
constructor(cfg: any);
options: any;
horizontal: any;
base: any;
width: any;
height: any;
inflateAmount: any;
draw(ctx: any): void;
inRange(mouseX: any, mouseY: any, useFinalPosition: any): boolean;
inXRange(mouseX: any, useFinalPosition: any): boolean;
inYRange(mouseY: any, useFinalPosition: any): boolean;
getCenterPoint(useFinalPosition: any): {
x: number;
y: number;
};
getRange(axis: any): number;
}
export type BarProps = {
x: number;
y: number;
base: number;
horizontal: boolean;
width: number;
height: number;
};
import Element from "../core/core.element.js";
+87
View File
@@ -0,0 +1,87 @@
export default class LineElement extends Element<import("../types/basic.js").AnyObject, import("../types/basic.js").AnyObject> {
static id: string;
/**
* @type {any}
*/
static defaults: any;
static descriptors: {
_scriptable: boolean;
_indexable: (name: any) => boolean;
};
constructor(cfg: any);
animated: boolean;
options: any;
_chart: any;
_loop: any;
_fullLoop: any;
_path: any;
_points: any;
_segments: import("../helpers/helpers.segment.js").Segment[];
_decimated: boolean;
_pointsUpdated: boolean;
_datasetIndex: any;
updateControlPoints(chartArea: any, indexAxis: any): void;
set points(arg: any);
get points(): any;
get segments(): import("../helpers/helpers.segment.js").Segment[];
/**
* First non-skipped point on this line
* @returns {PointElement|undefined}
*/
first(): PointElement | undefined;
/**
* Last non-skipped point on this line
* @returns {PointElement|undefined}
*/
last(): PointElement | undefined;
/**
* Interpolate a point in this line at the same value on `property` as
* the reference `point` provided
* @param {PointElement} point - the reference point
* @param {string} property - the property to match on
* @returns {PointElement|undefined}
*/
interpolate(point: PointElement, property: string): PointElement | undefined;
/**
* Append a segment of this line to current path.
* @param {CanvasRenderingContext2D} ctx
* @param {object} segment
* @param {number} segment.start - start index of the segment, referring the points array
* @param {number} segment.end - end index of the segment, referring the points array
* @param {boolean} segment.loop - indicates that the segment is a loop
* @param {object} params
* @param {boolean} params.move - move to starting point (vs line to it)
* @param {boolean} params.reverse - path the segment from end to start
* @param {number} params.start - limit segment to points starting from `start` index
* @param {number} params.end - limit segment to points ending at `start` + `count` index
* @returns {undefined|boolean} - true if the segment is a full loop (path should be closed)
*/
pathSegment(ctx: CanvasRenderingContext2D, segment: {
start: number;
end: number;
loop: boolean;
}, params: {
move: boolean;
reverse: boolean;
start: number;
end: number;
}): undefined | boolean;
/**
* Append all segments of this line to current path.
* @param {CanvasRenderingContext2D|Path2D} ctx
* @param {number} [start]
* @param {number} [count]
* @returns {undefined|boolean} - true if line is a full loop (path should be closed)
*/
path(ctx: CanvasRenderingContext2D | Path2D, start?: number, count?: number): undefined | boolean;
/**
* Draw
* @param {CanvasRenderingContext2D} ctx
* @param {object} chartArea
* @param {number} [start]
* @param {number} [count]
*/
draw(ctx: CanvasRenderingContext2D, chartArea: object, start?: number, count?: number): void;
}
export type PointElement = import('./element.point.js').default;
import Element from "../core/core.element.js";
@@ -0,0 +1,39 @@
import Element from '../core/core.element.js';
import type { CartesianParsedData, ChartArea, Point, PointHoverOptions, PointOptions } from '../types/index.js';
export type PointProps = Point;
export default class PointElement extends Element<PointProps, PointOptions & PointHoverOptions> {
static id: string;
parsed: CartesianParsedData;
skip?: boolean;
stop?: boolean;
/**
* @type {any}
*/
static defaults: {
borderWidth: number;
hitRadius: number;
hoverBorderWidth: number;
hoverRadius: number;
pointStyle: string;
radius: number;
rotation: number;
};
/**
* @type {any}
*/
static defaultRoutes: {
backgroundColor: string;
borderColor: string;
};
constructor(cfg: any);
inRange(mouseX: number, mouseY: number, useFinalPosition?: boolean): boolean;
inXRange(mouseX: number, useFinalPosition?: boolean): boolean;
inYRange(mouseY: number, useFinalPosition?: boolean): boolean;
getCenterPoint(useFinalPosition?: boolean): {
x: number;
y: number;
};
size(options?: Partial<PointOptions & PointHoverOptions>): number;
draw(ctx: CanvasRenderingContext2D, area: ChartArea): void;
getRange(): any;
}
+4
View File
@@ -0,0 +1,4 @@
export { default as ArcElement } from "./element.arc.js";
export { default as LineElement } from "./element.line.js";
export { default as PointElement } from "./element.point.js";
export { default as BarElement } from "./element.bar.js";
+136
View File
@@ -0,0 +1,136 @@
/*!
* Chart.js v4.5.1
* https://www.chartjs.org
* (c) 2025 Chart.js Contributors
* Released under the MIT License
*/
'use strict';
var helpers_dataset = require('./chunks/helpers.dataset.cjs');
require('@kurkle/color');
exports.HALF_PI = helpers_dataset.HALF_PI;
exports.INFINITY = helpers_dataset.INFINITY;
exports.PI = helpers_dataset.PI;
exports.PITAU = helpers_dataset.PITAU;
exports.QUARTER_PI = helpers_dataset.QUARTER_PI;
exports.RAD_PER_DEG = helpers_dataset.RAD_PER_DEG;
exports.TAU = helpers_dataset.TAU;
exports.TWO_THIRDS_PI = helpers_dataset.TWO_THIRDS_PI;
exports._addGrace = helpers_dataset._addGrace;
exports._alignPixel = helpers_dataset._alignPixel;
exports._alignStartEnd = helpers_dataset._alignStartEnd;
exports._angleBetween = helpers_dataset._angleBetween;
exports._angleDiff = helpers_dataset._angleDiff;
exports._arrayUnique = helpers_dataset._arrayUnique;
exports._attachContext = helpers_dataset._attachContext;
exports._bezierCurveTo = helpers_dataset._bezierCurveTo;
exports._bezierInterpolation = helpers_dataset._bezierInterpolation;
exports._boundSegment = helpers_dataset._boundSegment;
exports._boundSegments = helpers_dataset._boundSegments;
exports._capitalize = helpers_dataset._capitalize;
exports._computeSegments = helpers_dataset._computeSegments;
exports._createResolver = helpers_dataset._createResolver;
exports._decimalPlaces = helpers_dataset._decimalPlaces;
exports._deprecated = helpers_dataset._deprecated;
exports._descriptors = helpers_dataset._descriptors;
exports._elementsEqual = helpers_dataset._elementsEqual;
exports._factorize = helpers_dataset._factorize;
exports._filterBetween = helpers_dataset._filterBetween;
exports._getParentNode = helpers_dataset._getParentNode;
exports._getStartAndCountOfVisiblePoints = helpers_dataset._getStartAndCountOfVisiblePoints;
exports._int16Range = helpers_dataset._int16Range;
exports._isBetween = helpers_dataset._isBetween;
exports._isClickEvent = helpers_dataset._isClickEvent;
exports._isDomSupported = helpers_dataset._isDomSupported;
exports._isPointInArea = helpers_dataset._isPointInArea;
exports._limitValue = helpers_dataset._limitValue;
exports._longestText = helpers_dataset._longestText;
exports._lookup = helpers_dataset._lookup;
exports._lookupByKey = helpers_dataset._lookupByKey;
exports._measureText = helpers_dataset._measureText;
exports._merger = helpers_dataset._merger;
exports._mergerIf = helpers_dataset._mergerIf;
exports._normalizeAngle = helpers_dataset._normalizeAngle;
exports._parseObjectDataRadialScale = helpers_dataset._parseObjectDataRadialScale;
exports._pointInLine = helpers_dataset._pointInLine;
exports._readValueToProps = helpers_dataset._readValueToProps;
exports._rlookupByKey = helpers_dataset._rlookupByKey;
exports._scaleRangesChanged = helpers_dataset._scaleRangesChanged;
exports._setMinAndMaxByKey = helpers_dataset._setMinAndMaxByKey;
exports._splitKey = helpers_dataset._splitKey;
exports._steppedInterpolation = helpers_dataset._steppedInterpolation;
exports._steppedLineTo = helpers_dataset._steppedLineTo;
exports._textX = helpers_dataset._textX;
exports._toLeftRightCenter = helpers_dataset._toLeftRightCenter;
exports._updateBezierControlPoints = helpers_dataset._updateBezierControlPoints;
exports.addRoundedRectPath = helpers_dataset.addRoundedRectPath;
exports.almostEquals = helpers_dataset.almostEquals;
exports.almostWhole = helpers_dataset.almostWhole;
exports.callback = helpers_dataset.callback;
exports.clearCanvas = helpers_dataset.clearCanvas;
exports.clipArea = helpers_dataset.clipArea;
exports.clone = helpers_dataset.clone;
exports.color = helpers_dataset.color;
exports.createContext = helpers_dataset.createContext;
exports.debounce = helpers_dataset.debounce;
exports.defined = helpers_dataset.defined;
exports.distanceBetweenPoints = helpers_dataset.distanceBetweenPoints;
exports.drawPoint = helpers_dataset.drawPoint;
exports.drawPointLegend = helpers_dataset.drawPointLegend;
exports.each = helpers_dataset.each;
exports.easingEffects = helpers_dataset.effects;
exports.finiteOrDefault = helpers_dataset.finiteOrDefault;
exports.fontString = helpers_dataset.fontString;
exports.formatNumber = helpers_dataset.formatNumber;
exports.getAngleFromPoint = helpers_dataset.getAngleFromPoint;
exports.getDatasetClipArea = helpers_dataset.getDatasetClipArea;
exports.getHoverColor = helpers_dataset.getHoverColor;
exports.getMaximumSize = helpers_dataset.getMaximumSize;
exports.getRelativePosition = helpers_dataset.getRelativePosition;
exports.getRtlAdapter = helpers_dataset.getRtlAdapter;
exports.getStyle = helpers_dataset.getStyle;
exports.isArray = helpers_dataset.isArray;
exports.isFinite = helpers_dataset.isNumberFinite;
exports.isFunction = helpers_dataset.isFunction;
exports.isNullOrUndef = helpers_dataset.isNullOrUndef;
exports.isNumber = helpers_dataset.isNumber;
exports.isObject = helpers_dataset.isObject;
exports.isPatternOrGradient = helpers_dataset.isPatternOrGradient;
exports.listenArrayEvents = helpers_dataset.listenArrayEvents;
exports.log10 = helpers_dataset.log10;
exports.merge = helpers_dataset.merge;
exports.mergeIf = helpers_dataset.mergeIf;
exports.niceNum = helpers_dataset.niceNum;
exports.noop = helpers_dataset.noop;
exports.overrideTextDirection = helpers_dataset.overrideTextDirection;
exports.readUsedSize = helpers_dataset.readUsedSize;
exports.renderText = helpers_dataset.renderText;
exports.requestAnimFrame = helpers_dataset.requestAnimFrame;
exports.resolve = helpers_dataset.resolve;
exports.resolveObjectKey = helpers_dataset.resolveObjectKey;
exports.restoreTextDirection = helpers_dataset.restoreTextDirection;
exports.retinaScale = helpers_dataset.retinaScale;
exports.setsEqual = helpers_dataset.setsEqual;
exports.sign = helpers_dataset.sign;
exports.splineCurve = helpers_dataset.splineCurve;
exports.splineCurveMonotone = helpers_dataset.splineCurveMonotone;
exports.supportsEventListenerOptions = helpers_dataset.supportsEventListenerOptions;
exports.throttled = helpers_dataset.throttled;
exports.toDegrees = helpers_dataset.toDegrees;
exports.toDimension = helpers_dataset.toDimension;
exports.toFont = helpers_dataset.toFont;
exports.toFontString = helpers_dataset.toFontString;
exports.toLineHeight = helpers_dataset.toLineHeight;
exports.toPadding = helpers_dataset.toPadding;
exports.toPercentage = helpers_dataset.toPercentage;
exports.toRadians = helpers_dataset.toRadians;
exports.toTRBL = helpers_dataset.toTRBL;
exports.toTRBLCorners = helpers_dataset.toTRBLCorners;
exports.uid = helpers_dataset.uid;
exports.unclipArea = helpers_dataset.unclipArea;
exports.unlistenArrayEvents = helpers_dataset.unlistenArrayEvents;
exports.valueOrDefault = helpers_dataset.valueOrDefault;
//# sourceMappingURL=helpers.cjs.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"helpers.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
+9
View File
@@ -0,0 +1,9 @@
/*!
* Chart.js v4.5.1
* https://www.chartjs.org
* (c) 2025 Chart.js Contributors
* Released under the MIT License
*/
export { H as HALF_PI, b3 as INFINITY, P as PI, b2 as PITAU, b5 as QUARTER_PI, b4 as RAD_PER_DEG, T as TAU, b6 as TWO_THIRDS_PI, R as _addGrace, X as _alignPixel, a2 as _alignStartEnd, p as _angleBetween, b7 as _angleDiff, _ as _arrayUnique, a8 as _attachContext, au as _bezierCurveTo, ar as _bezierInterpolation, az as _boundSegment, ap as _boundSegments, a5 as _capitalize, ao as _computeSegments, a9 as _createResolver, aL as _decimalPlaces, aW as _deprecated, aa as _descriptors, ai as _elementsEqual, N as _factorize, aP as _filterBetween, I as _getParentNode, q as _getStartAndCountOfVisiblePoints, W as _int16Range, ak as _isBetween, aj as _isClickEvent, M as _isDomSupported, C as _isPointInArea, S as _limitValue, aO as _longestText, aQ as _lookup, B as _lookupByKey, V as _measureText, aU as _merger, aV as _mergerIf, al as _normalizeAngle, y as _parseObjectDataRadialScale, as as _pointInLine, am as _readValueToProps, A as _rlookupByKey, w as _scaleRangesChanged, aH as _setMinAndMaxByKey, aX as _splitKey, aq as _steppedInterpolation, at as _steppedLineTo, aC as _textX, a1 as _toLeftRightCenter, an as _updateBezierControlPoints, aw as addRoundedRectPath, aK as almostEquals, aJ as almostWhole, Q as callback, af as clearCanvas, Y as clipArea, aT as clone, c as color, j as createContext, ad as debounce, h as defined, aF as distanceBetweenPoints, av as drawPoint, aE as drawPointLegend, F as each, e as easingEffects, O as finiteOrDefault, b0 as fontString, o as formatNumber, D as getAngleFromPoint, ah as getDatasetClipArea, aS as getHoverColor, G as getMaximumSize, z as getRelativePosition, aA as getRtlAdapter, a$ as getStyle, b as isArray, g as isFinite, a7 as isFunction, k as isNullOrUndef, x as isNumber, i as isObject, aR as isPatternOrGradient, l as listenArrayEvents, aN as log10, a4 as merge, ab as mergeIf, aI as niceNum, aG as noop, aB as overrideTextDirection, J as readUsedSize, Z as renderText, r as requestAnimFrame, a as resolve, f as resolveObjectKey, aD as restoreTextDirection, ae as retinaScale, ag as setsEqual, s as sign, aZ as splineCurve, a_ as splineCurveMonotone, K as supportsEventListenerOptions, L as throttled, U as toDegrees, n as toDimension, a0 as toFont, aY as toFontString, b1 as toLineHeight, E as toPadding, m as toPercentage, t as toRadians, ax as toTRBL, ay as toTRBLCorners, ac as uid, $ as unclipArea, u as unlistenArrayEvents, v as valueOrDefault } from './chunks/helpers.dataset.js';
import '@kurkle/color';
//# sourceMappingURL=helpers.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"helpers.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;"}
@@ -0,0 +1,75 @@
import type { Chart, Point, FontSpec, CanvasFontSpec, PointStyle, RenderTextOpts } from '../types/index.js';
import type { TRBL, SplinePoint, RoundedRect, TRBLCorners } from '../types/geometric.js';
/**
* Converts the given font object into a CSS font string.
* @param font - A font object.
* @return The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font
* @private
*/
export declare function toFontString(font: FontSpec): string;
/**
* @private
*/
export declare function _measureText(ctx: CanvasRenderingContext2D, data: Record<string, number>, gc: string[], longest: number, string: string): number;
type Thing = string | undefined | null;
type Things = (Thing | Thing[])[];
/**
* @private
*/
export declare function _longestText(ctx: CanvasRenderingContext2D, font: string, arrayOfThings: Things, cache?: {
data?: Record<string, number>;
garbageCollect?: string[];
font?: string;
}): number;
/**
* Returns the aligned pixel value to avoid anti-aliasing blur
* @param chart - The chart instance.
* @param pixel - A pixel value.
* @param width - The width of the element.
* @returns The aligned pixel value.
* @private
*/
export declare function _alignPixel(chart: Chart, pixel: number, width: number): number;
/**
* Clears the entire canvas.
*/
export declare function clearCanvas(canvas?: HTMLCanvasElement, ctx?: CanvasRenderingContext2D): void;
export interface DrawPointOptions {
pointStyle: PointStyle;
rotation?: number;
radius: number;
borderWidth: number;
}
export declare function drawPoint(ctx: CanvasRenderingContext2D, options: DrawPointOptions, x: number, y: number): void;
export declare function drawPointLegend(ctx: CanvasRenderingContext2D, options: DrawPointOptions, x: number, y: number, w: number): void;
/**
* Returns true if the point is inside the rectangle
* @param point - The point to test
* @param area - The rectangle
* @param margin - allowed margin
* @private
*/
export declare function _isPointInArea(point: Point, area: TRBL, margin?: number): boolean;
export declare function clipArea(ctx: CanvasRenderingContext2D, area: TRBL): void;
export declare function unclipArea(ctx: CanvasRenderingContext2D): void;
/**
* @private
*/
export declare function _steppedLineTo(ctx: CanvasRenderingContext2D, previous: Point, target: Point, flip?: boolean, mode?: string): void;
/**
* @private
*/
export declare function _bezierCurveTo(ctx: CanvasRenderingContext2D, previous: SplinePoint, target: SplinePoint, flip?: boolean): void;
/**
* Render text onto the canvas
*/
export declare function renderText(ctx: CanvasRenderingContext2D, text: string | string[], x: number, y: number, font: CanvasFontSpec, opts?: RenderTextOpts): void;
/**
* Add a path of a rectangle with rounded corners to the current sub-path
* @param ctx - Context
* @param rect - Bounding rect
*/
export declare function addRoundedRectPath(ctx: CanvasRenderingContext2D, rect: RoundedRect & {
radius: TRBLCorners;
}): void;
export {};
@@ -0,0 +1,68 @@
/**
* Binary search
* @param table - the table search. must be sorted!
* @param value - value to find
* @param cmp
* @private
*/
export declare function _lookup(table: number[], value: number, cmp?: (value: number) => boolean): {
lo: number;
hi: number;
};
export declare function _lookup<T>(table: T[], value: number, cmp: (value: number) => boolean): {
lo: number;
hi: number;
};
/**
* Binary search
* @param table - the table search. must be sorted!
* @param key - property name for the value in each entry
* @param value - value to find
* @param last - lookup last index
* @private
*/
export declare const _lookupByKey: (table: Record<string, number>[], key: string, value: number, last?: boolean) => {
lo: number;
hi: number;
};
/**
* Reverse binary search
* @param table - the table search. must be sorted!
* @param key - property name for the value in each entry
* @param value - value to find
* @private
*/
export declare const _rlookupByKey: (table: Record<string, number>[], key: string, value: number) => {
lo: number;
hi: number;
};
/**
* Return subset of `values` between `min` and `max` inclusive.
* Values are assumed to be in sorted order.
* @param values - sorted array of values
* @param min - min value
* @param max - max value
*/
export declare function _filterBetween(values: number[], min: number, max: number): number[];
export interface ArrayListener<T> {
_onDataPush?(...item: T[]): void;
_onDataPop?(): void;
_onDataShift?(): void;
_onDataSplice?(index: number, deleteCount: number, ...items: T[]): void;
_onDataUnshift?(...item: T[]): void;
}
/**
* Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice',
* 'unshift') and notify the listener AFTER the array has been altered. Listeners are
* called on the '_onData*' callbacks (e.g. _onDataPush, etc.) with same arguments.
*/
export declare function listenArrayEvents<T>(array: T[], listener: ArrayListener<T>): void;
/**
* Removes the given array event listener and cleanup extra attached properties (such as
* the _chartjs stub and overridden methods) if array doesn't have any more listeners.
*/
export declare function unlistenArrayEvents<T>(array: T[], listener: ArrayListener<T>): void;
/**
* @param items
*/
export declare function _arrayUnique<T>(items: T[]): T[];
+13
View File
@@ -0,0 +1,13 @@
import { Color } from '@kurkle/color';
export declare function isPatternOrGradient(value: unknown): value is CanvasPattern | CanvasGradient;
export declare function color(value: CanvasGradient): CanvasGradient;
export declare function color(value: CanvasPattern): CanvasPattern;
export declare function color(value: string | {
r: number;
g: number;
b: number;
a: number;
} | [number, number, number] | [number, number, number, number]): Color;
export declare function getHoverColor(value: CanvasGradient): CanvasGradient;
export declare function getHoverColor(value: CanvasPattern): CanvasPattern;
export declare function getHoverColor(value: string): string;
@@ -0,0 +1,31 @@
import type { AnyObject } from '../types/basic.js';
import type { ChartMeta } from '../types/index.js';
import type { ResolverObjectKey, ResolverCache, ResolverProxy, DescriptorDefaults, Descriptor, ContextProxy } from './helpers.config.types.js';
export * from './helpers.config.types.js';
/**
* Creates a Proxy for resolving raw values for options.
* @param scopes - The option scopes to look for values, in resolution order
* @param prefixes - The prefixes for values, in resolution order.
* @param rootScopes - The root option scopes
* @param fallback - Parent scopes fallback
* @param getTarget - callback for getting the target for changed values
* @returns Proxy
* @private
*/
export declare function _createResolver<T extends AnyObject[] = AnyObject[], R extends AnyObject[] = T>(scopes: T, prefixes?: string[], rootScopes?: R, fallback?: ResolverObjectKey, getTarget?: () => AnyObject): any;
/**
* Returns an Proxy for resolving option values with context.
* @param proxy - The Proxy returned by `_createResolver`
* @param context - Context object for scriptable/indexable options
* @param subProxy - The proxy provided for scriptable options
* @param descriptorDefaults - Defaults for descriptors
* @private
*/
export declare function _attachContext<T extends AnyObject[] = AnyObject[], R extends AnyObject[] = T>(proxy: ResolverProxy<T, R>, context: AnyObject, subProxy?: ResolverProxy<T, R>, descriptorDefaults?: DescriptorDefaults): ContextProxy<T, R>;
/**
* @private
*/
export declare function _descriptors(proxy: ResolverCache, defaults?: DescriptorDefaults): Descriptor;
export declare function _parseObjectDataRadialScale(meta: ChartMeta<'line' | 'scatter'>, data: AnyObject[], start: number, count: number): {
r: unknown;
}[];
@@ -0,0 +1,41 @@
import type { AnyObject } from '../types/basic.js';
import type { Merge } from '../types/utils.js';
export type ResolverObjectKey = string | boolean;
export interface ResolverCache<T extends AnyObject[] = AnyObject[], R extends AnyObject[] = T> {
[Symbol.toStringTag]: 'Object';
_cacheable: boolean;
_scopes: T;
_rootScopes: T | R;
_fallback: ResolverObjectKey;
_keys?: string[];
_scriptable?: boolean;
_indexable?: boolean;
_allKeys?: boolean;
_storage?: T[number];
_getTarget(): T[number];
override<S extends AnyObject>(scope: S): ResolverProxy<(T[number] | S)[], T | R>;
}
export type ResolverProxy<T extends AnyObject[] = AnyObject[], R extends AnyObject[] = T> = Merge<T[number]> & ResolverCache<T, R>;
export interface DescriptorDefaults {
scriptable: boolean;
indexable: boolean;
allKeys?: boolean;
}
export interface Descriptor {
allKeys: boolean;
scriptable: boolean;
indexable: boolean;
isScriptable(key: string): boolean;
isIndexable(key: string): boolean;
}
export interface ContextCache<T extends AnyObject[] = AnyObject[], R extends AnyObject[] = T> {
_cacheable: boolean;
_proxy: ResolverProxy<T, R>;
_context: AnyObject;
_subProxy: ResolverProxy<T, R>;
_stack: Set<string>;
_descriptors: Descriptor;
setContext(ctx: AnyObject): ContextProxy<T, R>;
override<S extends AnyObject>(scope: S): ContextProxy<(T[number] | S)[], T | R>;
}
export type ContextProxy<T extends AnyObject[] = AnyObject[], R extends AnyObject[] = T> = Merge<T[number]> & ContextCache<T, R>;
+147
View File
@@ -0,0 +1,147 @@
/**
* @namespace Chart.helpers
*/
import type { AnyObject } from '../types/basic.js';
import type { ActiveDataPoint, ChartEvent } from '../types/index.js';
/**
* An empty function that can be used, for example, for optional callback.
*/
export declare function noop(): void;
/**
* Returns a unique id, sequentially generated from a global variable.
*/
export declare const uid: () => number;
/**
* Returns true if `value` is neither null nor undefined, else returns false.
* @param value - The value to test.
* @since 2.7.0
*/
export declare function isNullOrUndef(value: unknown): value is null | undefined;
/**
* Returns true if `value` is an array (including typed arrays), else returns false.
* @param value - The value to test.
* @function
*/
export declare function isArray<T = unknown>(value: unknown): value is T[];
/**
* Returns true if `value` is an object (excluding null), else returns false.
* @param value - The value to test.
* @since 2.7.0
*/
export declare function isObject(value: unknown): value is AnyObject;
/**
* Returns true if `value` is a finite number, else returns false
* @param value - The value to test.
*/
declare function isNumberFinite(value: unknown): value is number;
export { isNumberFinite as isFinite, };
/**
* Returns `value` if finite, else returns `defaultValue`.
* @param value - The value to return if defined.
* @param defaultValue - The value to return if `value` is not finite.
*/
export declare function finiteOrDefault(value: unknown, defaultValue: number): number;
/**
* Returns `value` if defined, else returns `defaultValue`.
* @param value - The value to return if defined.
* @param defaultValue - The value to return if `value` is undefined.
*/
export declare function valueOrDefault<T>(value: T | undefined, defaultValue: T): T;
export declare const toPercentage: (value: number | string, dimension: number) => number;
export declare const toDimension: (value: number | string, dimension: number) => number;
/**
* Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the
* value returned by `fn`. If `fn` is not a function, this method returns undefined.
* @param fn - The function to call.
* @param args - The arguments with which `fn` should be called.
* @param [thisArg] - The value of `this` provided for the call to `fn`.
*/
export declare function callback<T extends (this: TA, ...restArgs: unknown[]) => R, TA, R>(fn: T | undefined, args: unknown[], thisArg?: TA): R | undefined;
/**
* Note(SB) for performance sake, this method should only be used when loopable type
* is unknown or in none intensive code (not called often and small loopable). Else
* it's preferable to use a regular for() loop and save extra function calls.
* @param loopable - The object or array to be iterated.
* @param fn - The function to call for each item.
* @param [thisArg] - The value of `this` provided for the call to `fn`.
* @param [reverse] - If true, iterates backward on the loopable.
*/
export declare function each<T, TA>(loopable: Record<string, T>, fn: (this: TA, v: T, i: string) => void, thisArg?: TA, reverse?: boolean): void;
export declare function each<T, TA>(loopable: T[], fn: (this: TA, v: T, i: number) => void, thisArg?: TA, reverse?: boolean): void;
/**
* Returns true if the `a0` and `a1` arrays have the same content, else returns false.
* @param a0 - The array to compare
* @param a1 - The array to compare
* @private
*/
export declare function _elementsEqual(a0: ActiveDataPoint[], a1: ActiveDataPoint[]): boolean;
/**
* Returns a deep copy of `source` without keeping references on objects and arrays.
* @param source - The value to clone.
*/
export declare function clone<T>(source: T): T;
/**
* The default merger when Chart.helpers.merge is called without merger option.
* Note(SB): also used by mergeConfig and mergeScaleConfig as fallback.
* @private
*/
export declare function _merger(key: string, target: AnyObject, source: AnyObject, options: AnyObject): void;
export interface MergeOptions {
merger?: (key: string, target: AnyObject, source: AnyObject, options?: AnyObject) => void;
}
/**
* Recursively deep copies `source` properties into `target` with the given `options`.
* IMPORTANT: `target` is not cloned and will be updated with `source` properties.
* @param target - The target object in which all sources are merged into.
* @param source - Object(s) to merge into `target`.
* @param [options] - Merging options:
* @param [options.merger] - The merge method (key, target, source, options)
* @returns The `target` object.
*/
export declare function merge<T>(target: T, source: [], options?: MergeOptions): T;
export declare function merge<T, S1>(target: T, source: S1, options?: MergeOptions): T & S1;
export declare function merge<T, S1>(target: T, source: [S1], options?: MergeOptions): T & S1;
export declare function merge<T, S1, S2>(target: T, source: [S1, S2], options?: MergeOptions): T & S1 & S2;
export declare function merge<T, S1, S2, S3>(target: T, source: [S1, S2, S3], options?: MergeOptions): T & S1 & S2 & S3;
export declare function merge<T, S1, S2, S3, S4>(target: T, source: [S1, S2, S3, S4], options?: MergeOptions): T & S1 & S2 & S3 & S4;
export declare function merge<T>(target: T, source: AnyObject[], options?: MergeOptions): AnyObject;
/**
* Recursively deep copies `source` properties into `target` *only* if not defined in target.
* IMPORTANT: `target` is not cloned and will be updated with `source` properties.
* @param target - The target object in which all sources are merged into.
* @param source - Object(s) to merge into `target`.
* @returns The `target` object.
*/
export declare function mergeIf<T>(target: T, source: []): T;
export declare function mergeIf<T, S1>(target: T, source: S1): T & S1;
export declare function mergeIf<T, S1>(target: T, source: [S1]): T & S1;
export declare function mergeIf<T, S1, S2>(target: T, source: [S1, S2]): T & S1 & S2;
export declare function mergeIf<T, S1, S2, S3>(target: T, source: [S1, S2, S3]): T & S1 & S2 & S3;
export declare function mergeIf<T, S1, S2, S3, S4>(target: T, source: [S1, S2, S3, S4]): T & S1 & S2 & S3 & S4;
export declare function mergeIf<T>(target: T, source: AnyObject[]): AnyObject;
/**
* Merges source[key] in target[key] only if target[key] is undefined.
* @private
*/
export declare function _mergerIf(key: string, target: AnyObject, source: AnyObject): void;
/**
* @private
*/
export declare function _deprecated(scope: string, value: unknown, previous: string, current: string): void;
/**
* @private
*/
export declare function _splitKey(key: string): string[];
export declare function resolveObjectKey(obj: AnyObject, key: string): any;
/**
* @private
*/
export declare function _capitalize(str: string): string;
export declare const defined: (value: unknown) => boolean;
export declare const isFunction: (value: unknown) => value is (...args: any[]) => any;
export declare const setsEqual: <T>(a: Set<T>, b: Set<T>) => boolean;
/**
* @param e - The event
* @private
*/
export declare function _isClickEvent(e: ChartEvent): boolean;
+17
View File
@@ -0,0 +1,17 @@
import type { ChartArea } from '../types/index.js';
import type { SplinePoint } from '../types/geometric.js';
export declare function splineCurve(firstPoint: SplinePoint, middlePoint: SplinePoint, afterPoint: SplinePoint, t: number): {
previous: SplinePoint;
next: SplinePoint;
};
/**
* This function calculates Bézier control points in a similar way than |splineCurve|,
* but preserves monotonicity of the provided data and ensures no local extremums are added
* between the dataset discrete points due to the interpolation.
* See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation
*/
export declare function splineCurveMonotone(points: SplinePoint[], indexAxis?: 'x' | 'y'): void;
/**
* @private
*/
export declare function _updateBezierControlPoints(points: SplinePoint[], options: any, area: ChartArea, loop: boolean, indexAxis: 'x' | 'y'): void;
@@ -0,0 +1,2 @@
import type { Chart, ChartMeta, TRBL } from '../types/index.js';
export declare function getDatasetClipArea(chart: Chart, meta: ChartMeta): TRBL | false;
+48
View File
@@ -0,0 +1,48 @@
import type PrivateChart from '../core/core.controller.js';
import type { Chart, ChartEvent } from '../types.js';
/**
* @private
*/
export declare function _isDomSupported(): boolean;
/**
* @private
*/
export declare function _getParentNode(domNode: HTMLCanvasElement): HTMLCanvasElement;
export declare function getStyle(el: HTMLElement, property: string): string;
/**
* Gets an event's x, y coordinates, relative to the chart area
* @param event
* @param chart
* @returns x and y coordinates of the event
*/
export declare function getRelativePosition(event: Event | ChartEvent | TouchEvent | MouseEvent, chart: Chart | PrivateChart): {
x: number;
y: number;
};
export declare function getMaximumSize(canvas: HTMLCanvasElement, bbWidth?: number, bbHeight?: number, aspectRatio?: number): {
width: number;
height: number;
};
/**
* @param chart
* @param forceRatio
* @param forceStyle
* @returns True if the canvas context size or transformation has changed.
*/
export declare function retinaScale(chart: Chart | PrivateChart, forceRatio: number, forceStyle?: boolean): boolean | void;
/**
* Detects support for options object argument in addEventListener.
* https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
* @private
*/
export declare const supportsEventListenerOptions: boolean;
/**
* The "used" size is the final value of a dimension property after all calculations have
* been performed. This method uses the computed style of `element` but returns undefined
* if the computed style is not expressed in pixels. That can happen in some cases where
* `element` has a size relative to its parent and this last one is not yet displayed,
* for example because of `display: none` on a parent node.
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
* @returns Size in pixels or undefined if unknown.
*/
export declare function readUsedSize(element: HTMLElement, property: 'width' | 'height'): number | undefined;
@@ -0,0 +1,40 @@
/**
* Easing functions adapted from Robert Penner's easing equations.
* @namespace Chart.helpers.easing.effects
* @see http://www.robertpenner.com/easing/
*/
declare const effects: {
readonly linear: (t: number) => number;
readonly easeInQuad: (t: number) => number;
readonly easeOutQuad: (t: number) => number;
readonly easeInOutQuad: (t: number) => number;
readonly easeInCubic: (t: number) => number;
readonly easeOutCubic: (t: number) => number;
readonly easeInOutCubic: (t: number) => number;
readonly easeInQuart: (t: number) => number;
readonly easeOutQuart: (t: number) => number;
readonly easeInOutQuart: (t: number) => number;
readonly easeInQuint: (t: number) => number;
readonly easeOutQuint: (t: number) => number;
readonly easeInOutQuint: (t: number) => number;
readonly easeInSine: (t: number) => number;
readonly easeOutSine: (t: number) => number;
readonly easeInOutSine: (t: number) => number;
readonly easeInExpo: (t: number) => number;
readonly easeOutExpo: (t: number) => number;
readonly easeInOutExpo: (t: number) => number;
readonly easeInCirc: (t: number) => number;
readonly easeOutCirc: (t: number) => number;
readonly easeInOutCirc: (t: number) => number;
readonly easeInElastic: (t: number) => number;
readonly easeOutElastic: (t: number) => number;
readonly easeInOutElastic: (t: number) => number;
readonly easeInBack: (t: number) => number;
readonly easeOutBack: (t: number) => number;
readonly easeInOutBack: (t: number) => number;
readonly easeInBounce: (t: number) => number;
readonly easeOutBounce: (t: number) => number;
readonly easeInOutBounce: (t: number) => number;
};
export type EasingFunction = keyof typeof effects;
export default effects;
@@ -0,0 +1,45 @@
import type { ChartMeta, PointElement } from '../types/index.js';
export declare function fontString(pixelSize: number, fontStyle: string, fontFamily: string): string;
/**
* Request animation polyfill
*/
export declare const requestAnimFrame: (((callback: FrameRequestCallback) => number) & typeof requestAnimationFrame) | ((callback: any) => any);
/**
* Throttles calling `fn` once per animation frame
* Latest arguments are used on the actual call
*/
export declare function throttled<TArgs extends Array<any>>(fn: (...args: TArgs) => void, thisArg: any): (...args: TArgs) => void;
/**
* Debounces calling `fn` for `delay` ms
*/
export declare function debounce<TArgs extends Array<any>>(fn: (...args: TArgs) => void, delay: number): (...args: TArgs) => number;
/**
* Converts 'start' to 'left', 'end' to 'right' and others to 'center'
* @private
*/
export declare const _toLeftRightCenter: (align: 'start' | 'end' | 'center') => "center" | "left" | "right";
/**
* Returns `start`, `end` or `(start + end) / 2` depending on `align`. Defaults to `center`
* @private
*/
export declare const _alignStartEnd: (align: 'start' | 'end' | 'center', start: number, end: number) => number;
/**
* Returns `left`, `right` or `(left + right) / 2` depending on `align`. Defaults to `left`
* @private
*/
export declare const _textX: (align: 'left' | 'right' | 'center', left: number, right: number, rtl: boolean) => number;
/**
* Return start and count of visible points.
* @private
*/
export declare function _getStartAndCountOfVisiblePoints(meta: ChartMeta<'line' | 'scatter'>, points: PointElement[], animationsDisabled: boolean): {
start: number;
count: number;
};
/**
* Checks if the scale ranges have changed.
* @param {object} meta - dataset meta.
* @returns {boolean}
* @private
*/
export declare function _scaleRangesChanged(meta: any): boolean;
@@ -0,0 +1,22 @@
import type { Point, SplinePoint } from '../types/geometric.js';
/**
* @private
*/
export declare function _pointInLine(p1: Point, p2: Point, t: number, mode?: any): {
x: number;
y: number;
};
/**
* @private
*/
export declare function _steppedInterpolation(p1: Point, p2: Point, t: number, mode: 'middle' | 'after' | unknown): {
x: number;
y: number;
};
/**
* @private
*/
export declare function _bezierInterpolation(p1: SplinePoint, p2: SplinePoint, t: number, mode?: any): {
x: number;
y: number;
};
@@ -0,0 +1 @@
export declare function formatNumber(num: number, locale: string, options?: Intl.NumberFormatOptions): string;
+84
View File
@@ -0,0 +1,84 @@
import type { Point } from '../types/geometric.js';
/**
* @alias Chart.helpers.math
* @namespace
*/
export declare const PI: number;
export declare const TAU: number;
export declare const PITAU: number;
export declare const INFINITY: number;
export declare const RAD_PER_DEG: number;
export declare const HALF_PI: number;
export declare const QUARTER_PI: number;
export declare const TWO_THIRDS_PI: number;
export declare const log10: (x: number) => number;
export declare const sign: (x: number) => number;
export declare function almostEquals(x: number, y: number, epsilon: number): boolean;
/**
* Implementation of the nice number algorithm used in determining where axis labels will go
*/
export declare function niceNum(range: number): number;
/**
* Returns an array of factors sorted from 1 to sqrt(value)
* @private
*/
export declare function _factorize(value: number): number[];
export declare function isNumber(n: unknown): n is number;
export declare function almostWhole(x: number, epsilon: number): boolean;
/**
* @private
*/
export declare function _setMinAndMaxByKey(array: Record<string, number>[], target: {
min: number;
max: number;
}, property: string): void;
export declare function toRadians(degrees: number): number;
export declare function toDegrees(radians: number): number;
/**
* Returns the number of decimal places
* i.e. the number of digits after the decimal point, of the value of this Number.
* @param x - A number.
* @returns The number of decimal places.
* @private
*/
export declare function _decimalPlaces(x: number): number;
export declare function getAngleFromPoint(centrePoint: Point, anglePoint: Point): {
angle: number;
distance: number;
};
export declare function distanceBetweenPoints(pt1: Point, pt2: Point): number;
/**
* Shortest distance between angles, in either direction.
* @private
*/
export declare function _angleDiff(a: number, b: number): number;
/**
* Normalize angle to be between 0 and 2*PI
* @private
*/
export declare function _normalizeAngle(a: number): number;
/**
* @private
*/
export declare function _angleBetween(angle: number, start: number, end: number, sameAngleIsFullCircle?: boolean): boolean;
/**
* Limit `value` between `min` and `max`
* @param value
* @param min
* @param max
* @private
*/
export declare function _limitValue(value: number, min: number, max: number): number;
/**
* @param {number} value
* @private
*/
export declare function _int16Range(value: number): number;
/**
* @param value
* @param start
* @param end
* @param [epsilon]
* @private
*/
export declare function _isBetween(value: number, start: number, end: number, epsilon?: number): boolean;
@@ -0,0 +1,97 @@
import type { ChartArea, FontSpec, Point } from '../types/index.js';
import type { TRBL, TRBLCorners } from '../types/geometric.js';
/**
* @alias Chart.helpers.options
* @namespace
*/
/**
* Converts the given line height `value` in pixels for a specific font `size`.
* @param value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em').
* @param size - The font size (in pixels) used to resolve relative `value`.
* @returns The effective line height in pixels (size * 1.2 if value is invalid).
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
* @since 2.7.0
*/
export declare function toLineHeight(value: number | string, size: number): number;
/**
* @param value
* @param props
*/
export declare function _readValueToProps<K extends string>(value: number | Record<K, number>, props: K[]): Record<K, number>;
export declare function _readValueToProps<K extends string, T extends string>(value: number | Record<K & T, number>, props: Record<T, K>): Record<T, number>;
/**
* Converts the given value into a TRBL object.
* @param value - If a number, set the value to all TRBL component,
* else, if an object, use defined properties and sets undefined ones to 0.
* x / y are shorthands for same value for left/right and top/bottom.
* @returns The padding values (top, right, bottom, left)
* @since 3.0.0
*/
export declare function toTRBL(value: number | TRBL | Point): Record<"left" | "top" | "bottom" | "right", number>;
/**
* Converts the given value into a TRBL corners object (similar with css border-radius).
* @param value - If a number, set the value to all TRBL corner components,
* else, if an object, use defined properties and sets undefined ones to 0.
* @returns The TRBL corner values (topLeft, topRight, bottomLeft, bottomRight)
* @since 3.0.0
*/
export declare function toTRBLCorners(value: number | TRBLCorners): Record<"topLeft" | "topRight" | "bottomLeft" | "bottomRight", number>;
/**
* Converts the given value into a padding object with pre-computed width/height.
* @param value - If a number, set the value to all TRBL component,
* else, if an object, use defined properties and sets undefined ones to 0.
* x / y are shorthands for same value for left/right and top/bottom.
* @returns The padding values (top, right, bottom, left, width, height)
* @since 2.7.0
*/
export declare function toPadding(value?: number | TRBL): ChartArea;
/**
* Parses font options and returns the font object.
* @param options - A object that contains font options to be parsed.
* @param fallback - A object that contains fallback font options.
* @return The font object.
* @private
*/
export declare function toFont(options: Partial<FontSpec>, fallback?: Partial<FontSpec>): {
family: string;
lineHeight: number;
size: number;
style: "normal" | "inherit" | "italic" | "oblique" | "initial";
weight: number | "bold" | "normal" | "lighter" | "bolder";
string: string;
};
/**
* Evaluates the given `inputs` sequentially and returns the first defined value.
* @param inputs - An array of values, falling back to the last value.
* @param context - If defined and the current value is a function, the value
* is called with `context` as first argument and the result becomes the new input.
* @param index - If defined and the current value is an array, the value
* at `index` become the new input.
* @param info - object to return information about resolution in
* @param info.cacheable - Will be set to `false` if option is not cacheable.
* @since 2.7.0
*/
export declare function resolve(inputs: Array<unknown>, context?: object, index?: number, info?: {
cacheable: boolean;
}): unknown;
/**
* @param minmax
* @param grace
* @param beginAtZero
* @private
*/
export declare function _addGrace(minmax: {
min: number;
max: number;
}, grace: number | string, beginAtZero: boolean): {
min: number;
max: number;
};
/**
* Create a context inheriting parentContext
* @param parentContext
* @param context
* @returns
*/
export declare function createContext<T extends object>(parentContext: null, context: T): T;
export declare function createContext<T extends object, P extends T>(parentContext: P, context: T): P & T;
+10
View File
@@ -0,0 +1,10 @@
export interface RTLAdapter {
x(x: number): number;
setWidth(w: number): void;
textAlign(align: 'center' | 'left' | 'right'): 'center' | 'left' | 'right';
xPlus(x: number, value: number): number;
leftForLtr(x: number, itemWidth: number): number;
}
export declare function getRtlAdapter(rtl: boolean, rectX: number, width: number): RTLAdapter;
export declare function overrideTextDirection(ctx: CanvasRenderingContext2D, direction: 'ltr' | 'rtl'): void;
export declare function restoreTextDirection(ctx: CanvasRenderingContext2D, original?: [string, string]): void;
@@ -0,0 +1,65 @@
/**
* Returns the sub-segment(s) of a line segment that fall in the given bounds
* @param {object} segment
* @param {number} segment.start - start index of the segment, referring the points array
* @param {number} segment.end - end index of the segment, referring the points array
* @param {boolean} segment.loop - indicates that the segment is a loop
* @param {object} [segment.style] - segment style
* @param {PointElement[]} points - the points that this segment refers to
* @param {object} [bounds]
* @param {string} bounds.property - the property of a `PointElement` we are bounding. `x`, `y` or `angle`.
* @param {number} bounds.start - start value of the property
* @param {number} bounds.end - end value of the property
* @private
**/
export function _boundSegment(segment: {
start: number;
end: number;
loop: boolean;
style?: object;
}, points: PointElement[], bounds?: {
property: string;
start: number;
end: number;
}): {
start: number;
end: number;
loop: boolean;
style?: object;
}[];
/**
* Returns the segments of the line that are inside given bounds
* @param {LineElement} line
* @param {object} [bounds]
* @param {string} bounds.property - the property we are bounding with. `x`, `y` or `angle`.
* @param {number} bounds.start - start value of the `property`
* @param {number} bounds.end - end value of the `property`
* @private
*/
export function _boundSegments(line: LineElement, bounds?: {
property: string;
start: number;
end: number;
}): {
start: number;
end: number;
loop: boolean;
style?: object;
}[];
/**
* Compute the continuous segments that define the whole line
* There can be skipped points within a segment, if spanGaps is true.
* @param {LineElement} line
* @param {object} [segmentOptions]
* @return {Segment[]}
* @private
*/
export function _computeSegments(line: LineElement, segmentOptions?: object): Segment[];
export type LineElement = import('../elements/element.line.js').default;
export type PointElement = import('../elements/element.point.js').default;
export type Segment = {
start: number;
end: number;
loop: boolean;
style?: any;
};
+16
View File
@@ -0,0 +1,16 @@
export * from './helpers.color.js';
export * from './helpers.core.js';
export * from './helpers.canvas.js';
export * from './helpers.collection.js';
export * from './helpers.config.js';
export * from './helpers.curve.js';
export * from './helpers.dom.js';
export { default as easingEffects } from './helpers.easing.js';
export * from './helpers.extras.js';
export * from './helpers.interpolation.js';
export * from './helpers.intl.js';
export * from './helpers.options.js';
export * from './helpers.math.js';
export * from './helpers.rtl.js';
export * from './helpers.segment.js';
export * from './helpers.dataset.js';
+12
View File
@@ -0,0 +1,12 @@
export * from './controllers/index.js';
export * from './core/index.js';
export * from './elements/index.js';
export * from './platform/index.js';
export * from './plugins/index.js';
export * from './scales/index.js';
import * as controllers from './controllers/index.js';
import * as elements from './elements/index.js';
import * as plugins from './plugins/index.js';
import * as scales from './scales/index.js';
export { controllers, elements, plugins, scales, };
export declare const registerables: (typeof controllers | typeof elements | typeof plugins | typeof scales)[];
+5
View File
@@ -0,0 +1,5 @@
/**
* @namespace Chart
*/
import Chart from './core/core.controller.js';
export default Chart;
+5
View File
@@ -0,0 +1,5 @@
export function _detectPlatform(canvas: any): typeof BasicPlatform | typeof DomPlatform;
import BasicPlatform from "./platform.basic.js";
import DomPlatform from "./platform.dom.js";
import BasePlatform from "./platform.base.js";
export { BasePlatform, BasicPlatform, DomPlatform };
@@ -0,0 +1,63 @@
/**
* @typedef { import('../core/core.controller.js').default } Chart
*/
/**
* Abstract class that allows abstracting platform dependencies away from the chart.
*/
export default class BasePlatform {
/**
* Called at chart construction time, returns a context2d instance implementing
* the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}.
* @param {HTMLCanvasElement} canvas - The canvas from which to acquire context (platform specific)
* @param {number} [aspectRatio] - The chart options
*/
acquireContext(canvas: HTMLCanvasElement, aspectRatio?: number): void;
/**
* Called at chart destruction time, releases any resources associated to the context
* previously returned by the acquireContext() method.
* @param {CanvasRenderingContext2D} context - The context2d instance
* @returns {boolean} true if the method succeeded, else false
*/
releaseContext(context: CanvasRenderingContext2D): boolean;
/**
* Registers the specified listener on the given chart.
* @param {Chart} chart - Chart from which to listen for event
* @param {string} type - The ({@link ChartEvent}) type to listen for
* @param {function} listener - Receives a notification (an object that implements
* the {@link ChartEvent} interface) when an event of the specified type occurs.
*/
addEventListener(chart: Chart, type: string, listener: Function): void;
/**
* Removes the specified listener previously registered with addEventListener.
* @param {Chart} chart - Chart from which to remove the listener
* @param {string} type - The ({@link ChartEvent}) type to remove
* @param {function} listener - The listener function to remove from the event target.
*/
removeEventListener(chart: Chart, type: string, listener: Function): void;
/**
* @returns {number} the current devicePixelRatio of the device this platform is connected to.
*/
getDevicePixelRatio(): number;
/**
* Returns the maximum size in pixels of given canvas element.
* @param {HTMLCanvasElement} element
* @param {number} [width] - content width of parent element
* @param {number} [height] - content height of parent element
* @param {number} [aspectRatio] - aspect ratio to maintain
*/
getMaximumSize(element: HTMLCanvasElement, width?: number, height?: number, aspectRatio?: number): {
width: number;
height: number;
};
/**
* @param {HTMLCanvasElement} canvas
* @returns {boolean} true if the canvas is attached to the platform, false if not.
*/
isAttached(canvas: HTMLCanvasElement): boolean;
/**
* Updates config with platform specific requirements
* @param {import('../core/core.config.js').default} config
*/
updateConfig(config: import('../core/core.config.js').default): void;
}
export type Chart = import('../core/core.controller.js').default;
@@ -0,0 +1,10 @@
/**
* Platform class for charts without access to the DOM or to many element properties
* This platform is used by default for any chart passed an OffscreenCanvas.
* @extends BasePlatform
*/
export default class BasicPlatform extends BasePlatform {
acquireContext(item: any): any;
updateConfig(config: any): void;
}
import BasePlatform from "./platform.base.js";
+19
View File
@@ -0,0 +1,19 @@
/**
* Platform class for charts that can access the DOM and global window/document properties
* @extends BasePlatform
*/
export default class DomPlatform extends BasePlatform {
/**
* @param {HTMLCanvasElement} canvas
* @param {number} [aspectRatio]
* @return {CanvasRenderingContext2D|null}
*/
acquireContext(canvas: HTMLCanvasElement, aspectRatio?: number): CanvasRenderingContext2D | null;
/**
* @param {Chart} chart
* @param {string} type
*/
removeEventListener(chart: Chart, type: string): void;
}
export type Chart = import('../core/core.controller.js').default;
import BasePlatform from "./platform.base.js";
+7
View File
@@ -0,0 +1,7 @@
export { default as Colors } from "./plugin.colors.js";
export { default as Decimation } from "./plugin.decimation.js";
export { default as Filler } from "./plugin.filler/index.js";
export { default as Legend } from "./plugin.legend.js";
export { default as SubTitle } from "./plugin.subtitle.js";
export { default as Title } from "./plugin.title.js";
export { default as Tooltip } from "./plugin.tooltip.js";
+11
View File
@@ -0,0 +1,11 @@
import type { Chart } from '../types.js';
export interface ColorsPluginOptions {
enabled?: boolean;
forceOverride?: boolean;
}
declare const _default: {
id: string;
defaults: ColorsPluginOptions;
beforeLayout(chart: Chart, _args: any, options: ColorsPluginOptions): void;
};
export default _default;
@@ -0,0 +1,10 @@
declare namespace _default {
const id: string;
namespace defaults {
const algorithm: string;
const enabled: boolean;
}
function beforeElementsUpdate(chart: any, args: any, options: any): void;
function destroy(chart: any): void;
}
export default _default;
@@ -0,0 +1 @@
export function _drawfill(ctx: any, source: any, area: any): void;
@@ -0,0 +1,14 @@
/**
* @param {PointElement[] | { x: number; y: number; }} boundary
* @param {LineElement} line
* @return {LineElement?}
*/
export function _createBoundaryLine(boundary: PointElement[] | {
x: number;
y: number;
}, line: LineElement): LineElement | null;
export function _shouldApplyFill(source: any): boolean;
export type Chart = import('../../core/core.controller.js').default;
export type Scale = import('../../core/core.scale.js').default;
export type PointElement = import('../../elements/element.point.js').default;
import { LineElement } from "../../elements/index.js";
@@ -0,0 +1,30 @@
/**
* @typedef { import('../../core/core.scale.js').default } Scale
* @typedef { import('../../elements/element.line.js').default } LineElement
* @typedef { import('../../types/index.js').FillTarget } FillTarget
* @typedef { import('../../types/index.js').ComplexFillTarget } ComplexFillTarget
*/
export function _resolveTarget(sources: any, index: any, propagate: any): any;
/**
* @param {LineElement} line
* @param {number} index
* @param {number} count
*/
export function _decodeFill(line: LineElement, index: number, count: number): any;
/**
* @param {FillTarget | ComplexFillTarget} fill
* @param {Scale} scale
* @returns {number | null}
*/
export function _getTargetPixel(fill: FillTarget | ComplexFillTarget, scale: Scale): number | null;
/**
* @param {FillTarget | ComplexFillTarget} fill
* @param {Scale} scale
* @param {number} startValue
* @returns {number | undefined}
*/
export function _getTargetValue(fill: FillTarget | ComplexFillTarget, scale: Scale, startValue: number): number | undefined;
export type Scale = import('../../core/core.scale.js').default;
export type LineElement = import('../../elements/element.line.js').default;
export type FillTarget = import('../../types/index.js').FillTarget;
export type ComplexFillTarget = import('../../types/index.js').ComplexFillTarget;
@@ -0,0 +1,36 @@
export function _segments(line: any, target: any, property: any): ({
source: any;
target: {
property: any;
start: any;
end: any;
};
start: any;
end: any;
} | {
source: {
start: number;
end: number;
loop: boolean;
style?: any;
};
target: {
start: number;
end: number;
loop: boolean;
style?: any;
};
start: {
[x: number]: any;
};
end: {
[x: number]: any;
};
})[];
export function _getBounds(property: any, first: any, last: any, loop: any): {
property: any;
start: any;
end: any;
};
export function _pointsFromSegments(boundary: any, line: any): any[];
export function _findSegmentEnd(start: any, end: any, points: any): any;
@@ -0,0 +1,9 @@
/**
* @typedef { import('../../core/core.controller.js').default } Chart
* @typedef { import('../../core/core.scale.js').default } Scale
* @typedef { import('../../elements/element.point.js').default } PointElement
*/
export function _getTarget(source: any): any;
export type Chart = import('../../core/core.controller.js').default;
export type Scale = import('../../core/core.scale.js').default;
export type PointElement = import('../../elements/element.point.js').default;
@@ -0,0 +1,14 @@
/**
* @param {{ chart: Chart; scale: Scale; index: number; line: LineElement; }} source
* @return {LineElement}
*/
export function _buildStackLine(source: {
chart: Chart;
scale: Scale;
index: number;
line: LineElement;
}): LineElement;
export type Chart = import('../../core/core.controller.js').default;
export type Scale = import('../../core/core.scale.js').default;
export type PointElement = import('../../elements/element.point.js').default;
import { LineElement } from "../../elements/index.js";
@@ -0,0 +1,12 @@
declare namespace _default {
const id: string;
function afterDatasetsUpdate(chart: any, _args: any, options: any): void;
function beforeDraw(chart: any, _args: any, options: any): void;
function beforeDatasetsDraw(chart: any, _args: any, options: any): void;
function beforeDatasetDraw(chart: any, args: any, options: any): void;
namespace defaults {
const propagate: boolean;
const drawTime: string;
}
}
export default _default;
@@ -0,0 +1,12 @@
export class simpleArc {
constructor(opts: any);
x: any;
y: any;
radius: any;
pathSegment(ctx: any, bounds: any, opts: any): boolean;
interpolate(point: any): {
x: any;
y: any;
angle: any;
};
}
+114
View File
@@ -0,0 +1,114 @@
export class Legend extends Element<import("../types/basic.js").AnyObject, import("../types/basic.js").AnyObject> {
/**
* @param {{ ctx: any; options: any; chart: any; }} config
*/
constructor(config: {
ctx: any;
options: any;
chart: any;
});
_added: boolean;
legendHitBoxes: any[];
/**
* @private
*/
private _hoveredItem;
doughnutMode: boolean;
chart: any;
options: any;
ctx: any;
legendItems: any;
columnSizes: any[];
lineWidths: number[];
maxHeight: any;
maxWidth: any;
top: any;
bottom: any;
left: any;
right: any;
height: any;
width: any;
_margins: any;
position: any;
weight: any;
fullSize: any;
update(maxWidth: any, maxHeight: any, margins: any): void;
setDimensions(): void;
buildLabels(): void;
fit(): void;
/**
* @private
*/
private _fitRows;
_fitCols(titleHeight: any, labelFont: any, boxWidth: any, _itemHeight: any): any;
adjustHitBoxes(): void;
isHorizontal(): boolean;
draw(): void;
/**
* @private
*/
private _draw;
/**
* @protected
*/
protected drawTitle(): void;
/**
* @private
*/
private _computeTitleHeight;
/**
* @private
*/
private _getLegendItemAt;
/**
* Handle an event
* @param {ChartEvent} e - The event to handle
*/
handleEvent(e: ChartEvent): void;
}
declare namespace _default {
export const id: string;
export { Legend as _element };
export function start(chart: any, _args: any, options: any): void;
export function stop(chart: any): void;
export function beforeUpdate(chart: any, _args: any, options: any): void;
export function afterUpdate(chart: any): void;
export function afterEvent(chart: any, args: any): void;
export namespace defaults {
const display: boolean;
const position: string;
const align: string;
const fullSize: boolean;
const reverse: boolean;
const weight: number;
function onClick(e: any, legendItem: any, legend: any): void;
const onHover: any;
const onLeave: any;
namespace labels {
function color(ctx: any): any;
const boxWidth: number;
const padding: number;
function generateLabels(chart: any): any;
}
namespace title {
export function color_1(ctx: any): any;
export { color_1 as color };
const display_1: boolean;
export { display_1 as display };
const position_1: string;
export { position_1 as position };
export const text: string;
}
}
export namespace descriptors {
export function _scriptable(name: any): boolean;
export namespace labels_1 {
export function _scriptable_1(name: any): boolean;
export { _scriptable_1 as _scriptable };
}
export { labels_1 as labels };
}
}
export default _default;
export type ChartEvent = import('../types/index.js').ChartEvent;
import Element from "../core/core.element.js";
@@ -0,0 +1,27 @@
declare namespace _default {
const id: string;
function start(chart: any, _args: any, options: any): void;
function stop(chart: any): void;
function beforeUpdate(chart: any, _args: any, options: any): void;
namespace defaults {
export const align: string;
export const display: boolean;
export namespace font {
const weight: string;
}
export const fullSize: boolean;
export const padding: number;
export const position: string;
export const text: string;
const weight_1: number;
export { weight_1 as weight };
}
namespace defaultRoutes {
const color: string;
}
namespace descriptors {
const _scriptable: boolean;
const _indexable: boolean;
}
}
export default _default;
+61
View File
@@ -0,0 +1,61 @@
export class Title extends Element<import("../types/basic.js").AnyObject, import("../types/basic.js").AnyObject> {
/**
* @param {{ ctx: any; options: any; chart: any; }} config
*/
constructor(config: {
ctx: any;
options: any;
chart: any;
});
chart: any;
options: any;
ctx: any;
_padding: import("../types.js").ChartArea;
top: number;
bottom: any;
left: number;
right: any;
width: any;
height: any;
position: any;
weight: any;
fullSize: any;
update(maxWidth: any, maxHeight: any): void;
isHorizontal(): boolean;
_drawArgs(offset: any): {
titleX: any;
titleY: any;
maxWidth: number;
rotation: number;
};
draw(): void;
}
declare namespace _default {
export const id: string;
export { Title as _element };
export function start(chart: any, _args: any, options: any): void;
export function stop(chart: any): void;
export function beforeUpdate(chart: any, _args: any, options: any): void;
export namespace defaults {
export const align: string;
export const display: boolean;
export namespace font {
const weight: string;
}
export const fullSize: boolean;
export const padding: number;
export const position: string;
export const text: string;
const weight_1: number;
export { weight_1 as weight };
}
export namespace defaultRoutes {
const color: string;
}
export namespace descriptors {
const _scriptable: boolean;
const _indexable: boolean;
}
}
export default _default;
import Element from "../core/core.element.js";
+288
View File
@@ -0,0 +1,288 @@
export class Tooltip extends Element<import("../types/basic.js").AnyObject, import("../types/basic.js").AnyObject> {
/**
* @namespace Chart.Tooltip.positioners
*/
static positioners: {
/**
* Average mode places the tooltip at the average position of the elements shown
*/
average(items: any): false | {
x: number;
y: number;
};
/**
* Gets the tooltip position nearest of the item nearest to the event position
*/
nearest(items: any, eventPosition: any): false | {
x: any;
y: any;
};
};
constructor(config: any);
opacity: number;
_active: any[];
_eventPosition: any;
_size: {
width: number;
height: number;
};
_cachedAnimations: Readonly<Animations>;
_tooltipItems: any[];
$animations: any;
$context: any;
chart: any;
options: any;
dataPoints: {
chart: import("../core/core.controller.js").default;
label: any;
parsed: any;
raw: any;
formattedValue: any;
dataset: any;
dataIndex: number;
datasetIndex: number;
element: Element<import("../types/basic.js").AnyObject, import("../types/basic.js").AnyObject>;
}[];
title: any;
beforeBody: any;
body: any[];
afterBody: any;
footer: any;
xAlign: any;
yAlign: any;
x: any;
y: any;
height: number;
width: number;
caretX: any;
caretY: any;
labelColors: any[];
labelPointStyles: any[];
labelTextColors: any[];
initialize(options: any): void;
/**
* @private
*/
private _resolveAnimations;
/**
* @protected
*/
protected getContext(): any;
getTitle(context: any, options: any): any;
getBeforeBody(tooltipItems: any, options: any): any;
getBody(tooltipItems: any, options: any): any[];
getAfterBody(tooltipItems: any, options: any): any;
getFooter(tooltipItems: any, options: any): any;
/**
* @private
*/
private _createItems;
update(changed: any, replay: any): void;
drawCaret(tooltipPoint: any, ctx: any, size: any, options: any): void;
getCaretPosition(tooltipPoint: any, size: any, options: any): {
x1: any;
x2: any;
x3: any;
y1: any;
y2: any;
y3: any;
};
drawTitle(pt: any, ctx: any, options: any): void;
/**
* @private
*/
private _drawColorBox;
drawBody(pt: any, ctx: any, options: any): void;
drawFooter(pt: any, ctx: any, options: any): void;
drawBackground(pt: any, ctx: any, tooltipSize: any, options: any): void;
/**
* Update x/y animation targets when _active elements are animating too
* @private
*/
private _updateAnimationTarget;
/**
* Determine if the tooltip will draw anything
* @returns {boolean} True if the tooltip will render
*/
_willRender(): boolean;
draw(ctx: any): void;
/**
* Get active elements in the tooltip
* @returns {Array} Array of elements that are active in the tooltip
*/
getActiveElements(): any[];
/**
* Set active elements in the tooltip
* @param {array} activeElements Array of active datasetIndex/index pairs.
* @param {object} eventPosition Synthetic event position used in positioning
*/
setActiveElements(activeElements: any[], eventPosition: object): void;
_ignoreReplayEvents: boolean;
/**
* Handle an event
* @param {ChartEvent} e - The event to handle
* @param {boolean} [replay] - This is a replayed event (from update)
* @param {boolean} [inChartArea] - The event is inside chartArea
* @returns {boolean} true if the tooltip changed
*/
handleEvent(e: ChartEvent, replay?: boolean, inChartArea?: boolean): boolean;
/**
* Helper for determining the active elements for event
* @param {ChartEvent} e - The event to handle
* @param {InteractionItem[]} lastActive - Previously active elements
* @param {boolean} [replay] - This is a replayed event (from update)
* @param {boolean} [inChartArea] - The event is inside chartArea
* @returns {InteractionItem[]} - Active elements
* @private
*/
private _getActiveElements;
/**
* Determine if the active elements + event combination changes the
* tooltip position
* @param {array} active - Active elements
* @param {ChartEvent} e - Event that triggered the position change
* @returns {boolean} True if the position has changed
*/
_positionChanged(active: any[], e: ChartEvent): boolean;
}
declare namespace _default {
export const id: string;
export { Tooltip as _element };
export { positioners };
export function afterInit(chart: any, _args: any, options: any): void;
export function beforeUpdate(chart: any, _args: any, options: any): void;
export function reset(chart: any, _args: any, options: any): void;
export function afterDraw(chart: any): void;
export function afterEvent(chart: any, args: any): void;
export namespace defaults {
export const enabled: boolean;
export const external: any;
export const position: string;
export const backgroundColor: string;
export const titleColor: string;
export namespace titleFont {
const weight: string;
}
export const titleSpacing: number;
export const titleMarginBottom: number;
export const titleAlign: string;
export const bodyColor: string;
export const bodySpacing: number;
export const bodyFont: {};
export const bodyAlign: string;
export const footerColor: string;
export const footerSpacing: number;
export const footerMarginTop: number;
export namespace footerFont {
const weight_1: string;
export { weight_1 as weight };
}
export const footerAlign: string;
export const padding: number;
export const caretPadding: number;
export const caretSize: number;
export const cornerRadius: number;
export function boxHeight(ctx: any, opts: any): any;
export function boxWidth(ctx: any, opts: any): any;
export const multiKeyBackground: string;
export const displayColors: boolean;
export const boxPadding: number;
export const borderColor: string;
export const borderWidth: number;
export namespace animation {
const duration: number;
const easing: string;
}
export namespace animations {
namespace numbers {
const type: string;
const properties: string[];
}
namespace opacity {
const easing_1: string;
export { easing_1 as easing };
const duration_1: number;
export { duration_1 as duration };
}
}
export { defaultCallbacks as callbacks };
}
export namespace defaultRoutes {
const bodyFont_1: string;
export { bodyFont_1 as bodyFont };
const footerFont_1: string;
export { footerFont_1 as footerFont };
const titleFont_1: string;
export { titleFont_1 as titleFont };
}
export namespace descriptors {
export function _scriptable(name: any): boolean;
export const _indexable: boolean;
export namespace callbacks {
const _scriptable_1: boolean;
export { _scriptable_1 as _scriptable };
const _indexable_1: boolean;
export { _indexable_1 as _indexable };
}
export namespace animation_1 {
const _fallback: boolean;
}
export { animation_1 as animation };
export namespace animations_1 {
const _fallback_1: string;
export { _fallback_1 as _fallback };
}
export { animations_1 as animations };
}
export const additionalOptionScopes: string[];
}
export default _default;
export type Chart = import('../platform/platform.base.js').Chart;
export type ChartEvent = import('../types/index.js').ChartEvent;
export type ActiveElement = import('../types/index.js').ActiveElement;
export type InteractionItem = import('../core/core.interaction.js').InteractionItem;
import Element from "../core/core.element.js";
import Animations from "../core/core.animations.js";
declare namespace positioners {
/**
* Average mode places the tooltip at the average position of the elements shown
*/
function average(items: any): false | {
x: number;
y: number;
};
/**
* Gets the tooltip position nearest of the item nearest to the event position
*/
function nearest(items: any, eventPosition: any): false | {
x: any;
y: any;
};
}
declare namespace defaultCallbacks {
export { noop as beforeTitle };
export function title(tooltipItems: any): any;
export { noop as afterTitle };
export { noop as beforeBody };
export { noop as beforeLabel };
export function label(tooltipItem: any): any;
export function labelColor(tooltipItem: any): {
borderColor: any;
backgroundColor: any;
borderWidth: any;
borderDash: any;
borderDashOffset: any;
borderRadius: number;
};
export function labelTextColor(): any;
export function labelPointStyle(tooltipItem: any): {
pointStyle: any;
rotation: any;
};
export { noop as afterLabel };
export { noop as afterBody };
export { noop as beforeFooter };
export { noop as footer };
export { noop as afterFooter };
}
import { noop } from "../helpers/helpers.core.js";
+6
View File
@@ -0,0 +1,6 @@
export { default as CategoryScale } from "./scale.category.js";
export { default as LinearScale } from "./scale.linear.js";
export { default as LogarithmicScale } from "./scale.logarithmic.js";
export { default as RadialLinearScale } from "./scale.radialLinear.js";
export { default as TimeScale } from "./scale.time.js";
export { default as TimeSeriesScale } from "./scale.timeseries.js";
+21
View File
@@ -0,0 +1,21 @@
export default class CategoryScale extends Scale {
static id: string;
/**
* @type {any}
*/
static defaults: any;
/** @type {number} */
_startValue: number;
_valueRange: number;
_addedLabels: any[];
init(scaleOptions: any): void;
parse(raw: any, index: any): number;
buildTicks(): {
value: any;
}[];
getLabelForValue(value: any): any;
getPixelForValue(value: any): number;
getPixelForTick(index: any): number;
getValueForPixel(pixel: any): number;
}
import Scale from "../core/core.scale.js";
+10
View File
@@ -0,0 +1,10 @@
export default class LinearScale extends LinearScaleBase {
static id: string;
/**
* @type {any}
*/
static defaults: any;
getPixelForValue(value: any): number;
getValueForPixel(pixel: any): number;
}
import LinearScaleBase from "./scale.linearbase.js";
@@ -0,0 +1,20 @@
export default class LinearScaleBase extends Scale {
/** @type {number} */
start: number;
/** @type {number} */
end: number;
/** @type {number} */
_startValue: number;
/** @type {number} */
_endValue: number;
_valueRange: number;
parse(raw: any, index: any): number;
handleTickRangeOptions(): void;
getTickLimit(): number;
/**
* @protected
*/
protected computeTickLimit(): number;
getLabelForValue(value: any): string;
}
import Scale from "../core/core.scale.js";
@@ -0,0 +1,25 @@
export default class LogarithmicScale extends Scale {
static id: string;
/**
* @type {any}
*/
static defaults: any;
/** @type {number} */
start: number;
/** @type {number} */
end: number;
/** @type {number} */
_startValue: number;
_valueRange: number;
parse(raw: any, index: any): number;
_zero: boolean;
handleTickRangeOptions(): void;
/**
* @param {number} value
* @return {string}
*/
getLabelForValue(value: number): string;
getPixelForValue(value: any): number;
getValueForPixel(pixel: any): number;
}
import Scale from "../core/core.scale.js";
@@ -0,0 +1,63 @@
export default class RadialLinearScale extends LinearScaleBase {
static id: string;
/**
* @type {any}
*/
static defaults: any;
static defaultRoutes: {
'angleLines.color': string;
'pointLabels.color': string;
'ticks.color': string;
};
static descriptors: {
angleLines: {
_fallback: string;
};
};
/** @type {number} */
xCenter: number;
/** @type {number} */
yCenter: number;
/** @type {number} */
drawingArea: number;
/** @type {string[]} */
_pointLabels: string[];
_pointLabelItems: any[];
_padding: import("../types.js").ChartArea;
generateTickLabels(ticks: any): void;
setCenterPoint(leftMovement: any, rightMovement: any, topMovement: any, bottomMovement: any): void;
getIndexAngle(index: any): number;
getDistanceFromCenterForValue(value: any): number;
getValueForDistanceFromCenter(distance: any): any;
getPointLabelContext(index: any): any;
getPointPosition(index: any, distanceFromCenter: any, additionalAngle?: number): {
x: number;
y: number;
angle: number;
};
getPointPositionForValue(index: any, value: any): {
x: number;
y: number;
angle: number;
};
getBasePosition(index: any): {
x: number;
y: number;
angle: number;
};
getPointLabelPosition(index: any): {
left: any;
top: any;
right: any;
bottom: any;
};
/**
* @protected
*/
protected drawGrid(): void;
/**
* @protected
*/
protected drawLabels(): void;
}
import LinearScaleBase from "./scale.linearbase.js";
+130
View File
@@ -0,0 +1,130 @@
export default class TimeScale extends Scale {
static id: string;
/**
* @type {any}
*/
static defaults: any;
/**
* @param {object} props
*/
constructor(props: object);
/** @type {{data: number[], labels: number[], all: number[]}} */
_cache: {
data: number[];
labels: number[];
all: number[];
};
/** @type {Unit} */
_unit: Unit;
/** @type {Unit=} */
_majorUnit: Unit | undefined;
_offsets: {};
_normalized: boolean;
_parseOpts: {
parser: any;
round: any;
isoWeekday: any;
};
init(scaleOpts: any, opts?: {}): void;
_adapter: DateAdapter;
/**
* @param {*} raw
* @param {number?} [index]
* @return {number}
*/
parse(raw: any, index?: number | null): number;
/**
* @private
*/
private _getLabelBounds;
/**
* Returns the start and end offsets from edges in the form of {start, end}
* where each value is a relative width to the scale and ranges between 0 and 1.
* They add extra margins on the both sides by scaling down the original scale.
* Offsets are added when the `offset` option is true.
* @param {number[]} timestamps
* @protected
*/
protected initOffsets(timestamps?: number[]): void;
/**
* Generates a maximum of `capacity` timestamps between min and max, rounded to the
* `minor` unit using the given scale time `options`.
* Important: this method can return ticks outside the min and max range, it's the
* responsibility of the calling code to clamp values if needed.
* @protected
*/
protected _generate(): number[];
/**
* @param {number} value
* @return {string}
*/
getLabelForValue(value: number): string;
/**
* @param {number} value
* @param {string|undefined} format
* @return {string}
*/
format(value: number, format: string | undefined): string;
/**
* Function to format an individual tick mark
* @param {number} time
* @param {number} index
* @param {object[]} ticks
* @param {string|undefined} [format]
* @return {string}
* @private
*/
private _tickFormatFunction;
/**
* @param {object[]} ticks
*/
generateTickLabels(ticks: object[]): void;
/**
* @param {number} value - Milliseconds since epoch (1 January 1970 00:00:00 UTC)
* @return {number}
*/
getDecimalForValue(value: number): number;
/**
* @param {number} value - Milliseconds since epoch (1 January 1970 00:00:00 UTC)
* @return {number}
*/
getPixelForValue(value: number): number;
/**
* @param {number} pixel
* @return {number}
*/
getValueForPixel(pixel: number): number;
/**
* @param {string} label
* @return {{w:number, h:number}}
* @private
*/
private _getLabelSize;
/**
* @param {number} exampleTime
* @return {number}
* @private
*/
private _getLabelCapacity;
/**
* @protected
*/
protected getDataTimestamps(): any;
/**
* @protected
*/
protected getLabelTimestamps(): number[];
/**
* @param {number[]} values
* @protected
*/
protected normalize(values: number[]): number[];
}
export type Unit = import('../core/core.adapters.js').TimeUnit;
export type Interval = {
common: boolean;
size: number;
steps?: number;
};
export type DateAdapter = import('../core/core.adapters.js').DateAdapter;
import Scale from "../core/core.scale.js";
@@ -0,0 +1,39 @@
export default TimeSeriesScale;
declare class TimeSeriesScale extends TimeScale {
/** @type {object[]} */
_table: object[];
/** @type {number} */
_minPos: number;
/** @type {number} */
_tableRange: number;
/**
* @protected
*/
protected initOffsets(): void;
/**
* Returns an array of {time, pos} objects used to interpolate a specific `time` or position
* (`pos`) on the scale, by searching entries before and after the requested value. `pos` is
* a decimal between 0 and 1: 0 being the start of the scale (left or top) and 1 the other
* extremity (left + width or top + height). Note that it would be more optimized to directly
* store pre-computed pixels, but the scale dimensions are not guaranteed at the time we need
* to create the lookup table. The table ALWAYS contains at least two items: min and max.
* @param {number[]} timestamps
* @return {object[]}
* @protected
*/
protected buildLookupTable(timestamps: number[]): object[];
/**
* Generates all timestamps defined in the data.
* Important: this method can return ticks outside the min and max range, it's the
* responsibility of the calling code to clamp values if needed.
* @protected
*/
protected _generate(): any;
/**
* Returns all timestamps
* @return {number[]}
* @private
*/
private _getTimestampsForTable;
}
import TimeScale from "./scale.time.js";
+10
View File
@@ -0,0 +1,10 @@
/**
* Temporary entry point of the types at the time of the transition.
* After transition done need to remove it in favor of index.ts
*/
export * from './index.js';
/**
* Explicitly re-exporting to resolve the ambiguity.
*/
export { BarController, BubbleController, DoughnutController, LineController, PieController, PolarAreaController, RadarController, ScatterController, Animation, Animations, Chart, DatasetController, Interaction, Scale, Ticks, defaults, layouts, registry, ArcElement, BarElement, LineElement, PointElement, BasePlatform, BasicPlatform, DomPlatform, Decimation, Filler, Legend, SubTitle, Title, Tooltip, CategoryScale, LinearScale, LogarithmicScale, RadialLinearScale, TimeScale, TimeSeriesScale, PluginOptionsByType, ElementOptionsByType, ChartDatasetProperties, UpdateModeEnum, registerables } from './types/index.js';
export * from './types/index.js';
+34
View File
@@ -0,0 +1,34 @@
import {Chart} from './index.js';
import {AnyObject} from './basic.js';
export declare class Animation {
constructor(cfg: AnyObject, target: AnyObject, prop: string, to?: unknown);
active(): boolean;
update(cfg: AnyObject, to: unknown, date: number): void;
cancel(): void;
tick(date: number): void;
readonly _to: unknown;
}
export interface AnimationEvent {
chart: Chart;
numSteps: number;
initial: boolean;
currentStep: number;
}
export declare class Animator {
listen(chart: Chart, event: 'complete' | 'progress', cb: (event: AnimationEvent) => void): void;
add(chart: Chart, items: readonly Animation[]): void;
has(chart: Chart): boolean;
start(chart: Chart): void;
running(chart: Chart): boolean;
stop(chart: Chart): void;
remove(chart: Chart): boolean;
}
export declare class Animations {
constructor(chart: Chart, animations: AnyObject);
configure(animations: AnyObject): void;
update(target: AnyObject, values: AnyObject): undefined | boolean;
}
+3
View File
@@ -0,0 +1,3 @@
export type AnyObject = Record<string, any>;
export type EmptyObject = Record<string, never>;
+1
View File
@@ -0,0 +1 @@
export type Color = string | CanvasGradient | CanvasPattern;
+52
View File
@@ -0,0 +1,52 @@
export interface ChartArea {
top: number;
left: number;
right: number;
bottom: number;
width: number;
height: number;
}
export interface Point {
x: number | null;
y: number | null;
}
export type TRBL = {
top: number;
right: number;
bottom: number;
left: number;
}
export type TRBLCorners = {
topLeft: number;
topRight: number;
bottomLeft: number;
bottomRight: number;
};
export type CornerRadius = number | Partial<TRBLCorners>;
export type RoundedRect = {
x: number;
y: number;
w: number;
h: number;
radius?: CornerRadius
}
export type Padding = Partial<TRBL> | number | Point;
export interface SplinePoint {
x: number;
y: number;
skip?: boolean;
// Both Bezier and monotone interpolations have these fields
// but they are added in different spots
cp1x?: number;
cp1y?: number;
cp2x?: number;
cp2y?: number;
}
+3885
View File
@@ -0,0 +1,3885 @@
/* eslint-disable @typescript-eslint/ban-types */
import {DeepPartial, DistributiveArray, UnionToIntersection} from './utils.js';
import {TimeUnit} from '../core/core.adapters.js';
import PointElement from '../elements/element.point.js';
import {EasingFunction} from '../helpers/helpers.easing.js';
import {AnimationEvent} from './animation.js';
import {AnyObject, EmptyObject} from './basic.js';
import {Color} from './color.js';
import Element from '../core/core.element.js';
import {ChartArea, Padding, Point} from './geometric.js';
import {LayoutItem, LayoutPosition} from './layout.js';
import {ColorsPluginOptions} from '../plugins/plugin.colors.js';
export {EasingFunction} from '../helpers/helpers.easing.js';
export {default as ArcElement, ArcProps} from '../elements/element.arc.js';
export {default as PointElement, PointProps} from '../elements/element.point.js';
export {Animation, Animations, Animator, AnimationEvent} from './animation.js';
export {Color} from './color.js';
export {ChartArea, Point, TRBL} from './geometric.js';
export {LayoutItem, LayoutPosition} from './layout.js';
export interface ScriptableContext<TType extends ChartType> {
active: boolean;
chart: Chart;
dataIndex: number;
dataset: UnionToIntersection<ChartDataset<TType>>;
datasetIndex: number;
type: string;
mode: string;
parsed: UnionToIntersection<ParsedDataType<TType>>;
raw: unknown;
}
export interface ScriptableLineSegmentContext {
type: 'segment',
p0: PointElement,
p1: PointElement,
p0DataIndex: number,
p1DataIndex: number,
datasetIndex: number
}
export type Scriptable<T, TContext> = T | ((ctx: TContext, options: AnyObject) => T | undefined);
export type ScriptableOptions<T, TContext> = { [P in keyof T]: Scriptable<T[P], TContext> };
export type ScriptableAndScriptableOptions<T, TContext> = Scriptable<T, TContext> | ScriptableOptions<T, TContext>;
export type ScriptableAndArray<T, TContext> = readonly T[] | Scriptable<T, TContext>;
export type ScriptableAndArrayOptions<T, TContext> = { [P in keyof T]: ScriptableAndArray<T[P], TContext> };
export interface ParsingOptions {
/**
* How to parse the dataset. The parsing can be disabled by specifying parsing: false at chart options or dataset. If parsing is disabled, data must be sorted and in the formats the associated chart type and scales use internally.
*/
parsing:
{
[key: string]: string;
}
| false;
/**
* Chart.js is fastest if you provide data with indices that are unique, sorted, and consistent across datasets and provide the normalized: true option to let Chart.js know that you have done so.
*/
normalized: boolean;
}
export interface ControllerDatasetOptions extends ParsingOptions {
/**
* The base axis of the chart. 'x' for vertical charts and 'y' for horizontal charts.
* @default 'x'
*/
indexAxis: 'x' | 'y';
/**
* How to clip relative to chartArea. Positive value allows overflow, negative value clips that many pixels inside chartArea. 0 = clip at chartArea. Clipping can also be configured per side: `clip: {left: 5, top: false, right: -2, bottom: 0}`
*/
clip: number | ChartArea | false;
/**
* The label for the dataset which appears in the legend and tooltips.
*/
label: string;
/**
* The drawing order of dataset. Also affects order for stacking, tooltip and legend.
*/
order: number;
/**
* The ID of the group to which this dataset belongs to (when stacked, each group will be a separate stack).
*/
stack: string;
/**
* Configures the visibility state of the dataset. Set it to true, to hide the dataset from the chart.
* @default false
*/
hidden: boolean;
}
export interface BarControllerDatasetOptions
extends ControllerDatasetOptions,
ScriptableAndArrayOptions<BarOptions, ScriptableContext<'bar'>>,
ScriptableAndArrayOptions<CommonHoverOptions, ScriptableContext<'bar'>>,
AnimationOptions<'bar'> {
/**
* The ID of the x axis to plot this dataset on.
*/
xAxisID: string;
/**
* The ID of the y axis to plot this dataset on.
*/
yAxisID: string;
/**
* Percent (0-1) of the available width each bar should be within the category width. 1.0 will take the whole category width and put the bars right next to each other.
* @default 0.9
*/
barPercentage: number;
/**
* Percent (0-1) of the available width each category should be within the sample width.
* @default 0.8
*/
categoryPercentage: number;
/**
* Manually set width of each bar in pixels. If set to 'flex', it computes "optimal" sample widths that globally arrange bars side by side. If not set (default), bars are equally sized based on the smallest interval.
*/
barThickness: number | 'flex';
/**
* Set this to ensure that bars are not sized thicker than this.
*/
maxBarThickness: number;
/**
* Set this to ensure that bars have a minimum length in pixels.
*/
minBarLength: number;
/**
* Point style for the legend
* @default 'circle;
*/
pointStyle: PointStyle;
/**
* Should the bars be grouped on index axis
* @default true
*/
grouped: boolean;
}
export interface BarControllerChartOptions {
/**
* Should null or undefined values be omitted from drawing
*/
skipNull?: boolean;
}
export type BarController = DatasetController
export declare const BarController: ChartComponent & {
prototype: BarController;
new (chart: Chart, datasetIndex: number): BarController;
};
export interface BubbleControllerDatasetOptions
extends ControllerDatasetOptions,
ScriptableAndArrayOptions<PointOptions, ScriptableContext<'bubble'>>,
ScriptableAndArrayOptions<PointHoverOptions, ScriptableContext<'bubble'>> {
/**
* The ID of the x axis to plot this dataset on.
*/
xAxisID: string;
/**
* The ID of the y axis to plot this dataset on.
*/
yAxisID: string;
}
export interface BubbleDataPoint extends Point {
/**
* Bubble radius in pixels (not scaled).
*/
r?: number;
}
export type BubbleController = DatasetController
export declare const BubbleController: ChartComponent & {
prototype: BubbleController;
new (chart: Chart, datasetIndex: number): BubbleController;
};
export interface LineControllerDatasetOptions
extends ControllerDatasetOptions,
ScriptableAndArrayOptions<PointPrefixedOptions, ScriptableContext<'line'>>,
ScriptableAndArrayOptions<PointPrefixedHoverOptions, ScriptableContext<'line'>>,
ScriptableOptions<Omit<LineOptions, keyof CommonElementOptions>, ScriptableContext<'line'>>,
ScriptableAndArrayOptions<CommonElementOptions, ScriptableContext<'line'>>,
ScriptableOptions<Omit<LineHoverOptions, keyof CommonHoverOptions>, ScriptableContext<'line'>>,
ScriptableAndArrayOptions<CommonHoverOptions, ScriptableContext<'line'>>,
AnimationOptions<'line'> {
/**
* The ID of the x axis to plot this dataset on.
*/
xAxisID: string;
/**
* The ID of the y axis to plot this dataset on.
*/
yAxisID: string;
/**
* If true, lines will be drawn between points with no or null data. If false, points with NaN data will create a break in the line. Can also be a number specifying the maximum gap length to span. The unit of the value depends on the scale used.
* @default false
*/
spanGaps: boolean | number;
showLine: boolean;
}
export interface LineControllerChartOptions {
/**
* If true, lines will be drawn between points with no or null data. If false, points with NaN data will create a break in the line. Can also be a number specifying the maximum gap length to span. The unit of the value depends on the scale used.
* @default false
*/
spanGaps: boolean | number;
/**
* If false, the lines between points are not drawn.
* @default true
*/
showLine: boolean;
}
export type LineController = DatasetController
export declare const LineController: ChartComponent & {
prototype: LineController;
new (chart: Chart, datasetIndex: number): LineController;
};
export type ScatterControllerDatasetOptions = LineControllerDatasetOptions;
export type ScatterDataPoint = Point
export type ScatterControllerChartOptions = LineControllerChartOptions;
export type ScatterController = LineController
export declare const ScatterController: ChartComponent & {
prototype: ScatterController;
new (chart: Chart, datasetIndex: number): ScatterController;
};
export interface DoughnutControllerDatasetOptions
extends ControllerDatasetOptions,
ScriptableAndArrayOptions<ArcOptions, ScriptableContext<'doughnut'>>,
ScriptableAndArrayOptions<ArcHoverOptions, ScriptableContext<'doughnut'>>,
AnimationOptions<'doughnut'> {
/**
* Sweep to allow arcs to cover.
* @default 360
*/
circumference: number;
/**
* Arc offset (in pixels).
*/
offset: number | number[];
/**
* Starting angle to draw this dataset from.
* @default 0
*/
rotation: number;
/**
* The relative thickness of the dataset. Providing a value for weight will cause the pie or doughnut dataset to be drawn with a thickness relative to the sum of all the dataset weight values.
* @default 1
*/
weight: number;
/**
* Similar to the `offset` option, but applies to all arcs. This can be used to to add spaces
* between arcs
* @default 0
*/
spacing: number;
}
export interface DoughnutAnimationOptions extends AnimationSpec<'doughnut'> {
/**
* If true, the chart will animate in with a rotation animation. This property is in the options.animation object.
* @default true
*/
animateRotate: boolean;
/**
* If true, will animate scaling the chart from the center outwards.
* @default false
*/
animateScale: boolean;
}
export interface DoughnutControllerChartOptions {
/**
* Sweep to allow arcs to cover.
* @default 360
*/
circumference: number;
/**
* The portion of the chart that is cut out of the middle. ('50%' - for doughnut, 0 - for pie)
* String ending with '%' means percentage, number means pixels.
* @default 50
*/
cutout: Scriptable<number | string, ScriptableContext<'doughnut'>>;
/**
* Arc offset (in pixels).
*/
offset: number | number[];
/**
* The outer radius of the chart. String ending with '%' means percentage of maximum radius, number means pixels.
* @default '100%'
*/
radius: Scriptable<number | string, ScriptableContext<'doughnut'>>;
/**
* Starting angle to draw arcs from.
* @default 0
*/
rotation: number;
/**
* Spacing between the arcs
* @default 0
*/
spacing: number;
animation: false | DoughnutAnimationOptions;
}
export type DoughnutDataPoint = number;
export interface DoughnutController extends DatasetController {
readonly innerRadius: number;
readonly outerRadius: number;
readonly offsetX: number;
readonly offsetY: number;
calculateTotal(): number;
calculateCircumference(value: number): number;
}
export declare const DoughnutController: ChartComponent & {
prototype: DoughnutController;
new (chart: Chart, datasetIndex: number): DoughnutController;
};
export interface DoughnutMetaExtensions {
total: number;
}
export type PieControllerDatasetOptions = DoughnutControllerDatasetOptions;
export type PieControllerChartOptions = DoughnutControllerChartOptions;
export type PieAnimationOptions = DoughnutAnimationOptions;
export type PieDataPoint = DoughnutDataPoint;
export type PieMetaExtensions = DoughnutMetaExtensions;
export type PieController = DoughnutController
export declare const PieController: ChartComponent & {
prototype: PieController;
new (chart: Chart, datasetIndex: number): PieController;
};
export interface PolarAreaControllerDatasetOptions extends DoughnutControllerDatasetOptions {
/**
* Arc angle to cover. - for polar only
* @default circumference / (arc count)
*/
angle: number;
}
export type PolarAreaAnimationOptions = DoughnutAnimationOptions;
export interface PolarAreaControllerChartOptions {
/**
* Starting angle to draw arcs for the first item in a dataset. In degrees, 0 is at top.
* @default 0
*/
startAngle: number;
animation: false | PolarAreaAnimationOptions;
}
export interface PolarAreaController extends DoughnutController {
countVisibleElements(): number;
}
export declare const PolarAreaController: ChartComponent & {
prototype: PolarAreaController;
new (chart: Chart, datasetIndex: number): PolarAreaController;
};
export interface RadarControllerDatasetOptions
extends ControllerDatasetOptions,
ScriptableAndArrayOptions<PointOptions & PointHoverOptions & PointPrefixedOptions & PointPrefixedHoverOptions, ScriptableContext<'radar'>>,
ScriptableAndArrayOptions<LineOptions & LineHoverOptions, ScriptableContext<'radar'>>,
AnimationOptions<'radar'> {
/**
* The ID of the x axis to plot this dataset on.
*/
xAxisID: string;
/**
* The ID of the y axis to plot this dataset on.
*/
yAxisID: string;
/**
* If true, lines will be drawn between points with no or null data. If false, points with NaN data will create a break in the line. Can also be a number specifying the maximum gap length to span. The unit of the value depends on the scale used.
*/
spanGaps: boolean | number;
/**
* If false, the line is not drawn for this dataset.
*/
showLine: boolean;
}
export type RadarControllerChartOptions = LineControllerChartOptions;
export type RadarController = DatasetController
export declare const RadarController: ChartComponent & {
prototype: RadarController;
new (chart: Chart, datasetIndex: number): RadarController;
};
interface ChartMetaClip {
left: number | boolean;
top: number | boolean;
right: number | boolean;
bottom: number | boolean;
disabled: boolean;
}
interface ChartMetaCommon<TElement extends Element = Element, TDatasetElement extends Element = Element> {
type: string;
controller: DatasetController;
order: number;
label: string;
index: number;
visible: boolean;
stack: number;
indexAxis: 'x' | 'y';
data: TElement[];
dataset?: TDatasetElement;
hidden: boolean;
xAxisID?: string;
yAxisID?: string;
rAxisID?: string;
iAxisID: string;
vAxisID: string;
xScale?: Scale;
yScale?: Scale;
rScale?: Scale;
iScale?: Scale;
vScale?: Scale;
_sorted: boolean;
_stacked: boolean | 'single';
_parsed: unknown[];
_clip: ChartMetaClip;
}
export type ChartMeta<
TType extends ChartType = ChartType,
TElement extends Element = Element,
TDatasetElement extends Element = Element,
> = DeepPartial<
{ [key in ChartType]: ChartTypeRegistry[key]['metaExtensions'] }[TType]
> & ChartMetaCommon<TElement, TDatasetElement>;
export interface ActiveDataPoint {
datasetIndex: number;
index: number;
}
export interface ActiveElement extends ActiveDataPoint {
element: Element;
}
export declare class Chart<
TType extends ChartType = ChartType,
TData = DefaultDataPoint<TType>,
TLabel = unknown
> {
readonly platform: BasePlatform;
readonly id: string;
readonly canvas: HTMLCanvasElement;
readonly ctx: CanvasRenderingContext2D;
readonly config: ChartConfiguration<TType, TData, TLabel> | ChartConfigurationCustomTypesPerDataset<TType, TData, TLabel>;
readonly width: number;
readonly height: number;
readonly aspectRatio: number;
readonly boxes: LayoutItem[];
readonly currentDevicePixelRatio: number;
readonly chartArea: ChartArea;
readonly scales: { [key: string]: Scale };
readonly attached: boolean;
readonly legend?: LegendElement<TType>; // Only available if legend plugin is registered and enabled
readonly tooltip?: TooltipModel<TType>; // Only available if tooltip plugin is registered and enabled
data: ChartData<TType, TData, TLabel>;
options: ChartOptions<TType>;
constructor(item: ChartItem, config: ChartConfiguration<TType, TData, TLabel> | ChartConfigurationCustomTypesPerDataset<TType, TData, TLabel>);
clear(): this;
stop(): this;
resize(width?: number, height?: number): void;
ensureScalesHaveIDs(): void;
buildOrUpdateScales(): void;
buildOrUpdateControllers(): void;
reset(): void;
update(mode?: UpdateMode | ((ctx: { datasetIndex: number }) => UpdateMode)): void;
render(): void;
draw(): void;
isPointInArea(point: Point): boolean;
getElementsAtEventForMode(e: Event, mode: string, options: InteractionOptions, useFinalPosition: boolean): InteractionItem[];
getSortedVisibleDatasetMetas(): ChartMeta[];
getDatasetMeta(datasetIndex: number): ChartMeta;
getVisibleDatasetCount(): number;
isDatasetVisible(datasetIndex: number): boolean;
setDatasetVisibility(datasetIndex: number, visible: boolean): void;
toggleDataVisibility(index: number): void;
getDataVisibility(index: number): boolean;
hide(datasetIndex: number, dataIndex?: number): void;
show(datasetIndex: number, dataIndex?: number): void;
getActiveElements(): ActiveElement[];
setActiveElements(active: ActiveDataPoint[]): void;
destroy(): void;
toBase64Image(type?: string, quality?: unknown): string;
bindEvents(): void;
unbindEvents(): void;
updateHoverStyle(items: InteractionItem[], mode: 'dataset', enabled: boolean): void;
notifyPlugins(hook: string, args?: AnyObject): boolean | void;
isPluginEnabled(pluginId: string): boolean;
getContext(): { chart: Chart, type: string };
static readonly defaults: Defaults;
static readonly overrides: Overrides;
static readonly version: string;
static readonly instances: { [key: string]: Chart };
static readonly registry: Registry;
static getChart(key: string | CanvasRenderingContext2D | HTMLCanvasElement): Chart | undefined;
static register(...items: ChartComponentLike[]): void;
static unregister(...items: ChartComponentLike[]): void;
}
export declare const registerables: readonly ChartComponentLike[];
export declare type ChartItem =
| string
| CanvasRenderingContext2D
| HTMLCanvasElement
| { canvas: HTMLCanvasElement }
| ArrayLike<CanvasRenderingContext2D | HTMLCanvasElement>;
export declare enum UpdateModeEnum {
resize = 'resize',
reset = 'reset',
none = 'none',
hide = 'hide',
show = 'show',
default = 'default',
active = 'active'
}
export type UpdateMode = keyof typeof UpdateModeEnum;
export declare class DatasetController<
TType extends ChartType = ChartType,
TElement extends Element = Element,
TDatasetElement extends Element = Element,
TParsedData = ParsedDataType<TType>,
> {
constructor(chart: Chart, datasetIndex: number);
readonly chart: Chart;
readonly index: number;
readonly _cachedMeta: ChartMeta<TType, TElement, TDatasetElement>;
enableOptionSharing: boolean;
// If true, the controller supports the decimation
// plugin. Defaults to `false` for all controllers
// except the LineController
supportsDecimation: boolean;
linkScales(): void;
getAllParsedValues(scale: Scale): number[];
protected getLabelAndValue(index: number): { label: string; value: string };
updateElements(elements: TElement[], start: number, count: number, mode: UpdateMode): void;
update(mode: UpdateMode): void;
updateIndex(datasetIndex: number): void;
protected getMaxOverflow(): boolean | number;
draw(): void;
reset(): void;
getDataset(): ChartDataset;
getMeta(): ChartMeta<TType, TElement, TDatasetElement>;
getScaleForId(scaleID: string): Scale | undefined;
configure(): void;
initialize(): void;
addElements(): void;
buildOrUpdateElements(resetNewElements?: boolean): void;
getStyle(index: number, active: boolean): AnyObject;
protected resolveDatasetElementOptions(mode: UpdateMode): AnyObject;
protected resolveDataElementOptions(index: number, mode: UpdateMode): AnyObject;
/**
* Utility for checking if the options are shared and should be animated separately.
* @protected
*/
protected getSharedOptions(options: AnyObject): undefined | AnyObject;
/**
* Utility for determining if `options` should be included in the updated properties
* @protected
*/
protected includeOptions(mode: UpdateMode, sharedOptions: AnyObject): boolean;
/**
* Utility for updating an element with new properties, using animations when appropriate.
* @protected
*/
protected updateElement(element: TElement | TDatasetElement, index: number | undefined, properties: AnyObject, mode: UpdateMode): void;
/**
* Utility to animate the shared options, that are potentially affecting multiple elements.
* @protected
*/
protected updateSharedOptions(sharedOptions: AnyObject, mode: UpdateMode, newOptions: AnyObject): void;
removeHoverStyle(element: TElement, datasetIndex: number, index: number): void;
setHoverStyle(element: TElement, datasetIndex: number, index: number): void;
parse(start: number, count: number): void;
protected parsePrimitiveData(meta: ChartMeta<TType, TElement, TDatasetElement>, data: AnyObject[], start: number, count: number): AnyObject[];
protected parseArrayData(meta: ChartMeta<TType, TElement, TDatasetElement>, data: AnyObject[], start: number, count: number): AnyObject[];
protected parseObjectData(meta: ChartMeta<TType, TElement, TDatasetElement>, data: AnyObject[], start: number, count: number): AnyObject[];
protected getParsed(index: number): TParsedData;
protected applyStack(scale: Scale, parsed: unknown[]): number;
protected updateRangeFromParsed(
range: { min: number; max: number },
scale: Scale,
parsed: unknown[],
stack: boolean | string
): void;
protected getMinMax(scale: Scale, canStack?: boolean): { min: number; max: number };
}
export interface DatasetControllerChartComponent extends ChartComponent {
defaults: {
datasetElementType?: string | null | false;
dataElementType?: string | null | false;
};
}
export interface Defaults extends CoreChartOptions<ChartType>, ElementChartOptions<ChartType>, PluginChartOptions<ChartType> {
scale: ScaleOptionsByType;
scales: {
[key in ScaleType]: ScaleOptionsByType<key>;
};
set(values: AnyObject): AnyObject;
set(scope: string, values: AnyObject): AnyObject;
get(scope: string): AnyObject;
describe(scope: string, values: AnyObject): AnyObject;
override(scope: string, values: AnyObject): AnyObject;
/**
* Routes the named defaults to fallback to another scope/name.
* This routing is useful when those target values, like defaults.color, are changed runtime.
* If the values would be copied, the runtime change would not take effect. By routing, the
* fallback is evaluated at each access, so its always up to date.
*
* Example:
*
* defaults.route('elements.arc', 'backgroundColor', '', 'color')
* - reads the backgroundColor from defaults.color when undefined locally
*
* @param scope Scope this route applies to.
* @param name Property name that should be routed to different namespace when not defined here.
* @param targetScope The namespace where those properties should be routed to.
* Empty string ('') is the root of defaults.
* @param targetName The target name in the target scope the property should be routed to.
*/
route(scope: string, name: string, targetScope: string, targetName: string): void;
}
export type Overrides = {
[key in ChartType]:
CoreChartOptions<key> &
ElementChartOptions<key> &
PluginChartOptions<key> &
DatasetChartOptions<ChartType> &
ScaleChartOptions<key> &
ChartTypeRegistry[key]['chartOptions'];
}
export declare const defaults: Defaults;
export interface InteractionOptions {
axis?: string;
intersect?: boolean;
includeInvisible?: boolean;
}
export interface InteractionItem {
element: Element;
datasetIndex: number;
index: number;
}
export type InteractionModeFunction = (
chart: Chart,
e: ChartEvent,
options: InteractionOptions,
useFinalPosition?: boolean
) => InteractionItem[];
export interface InteractionModeMap {
/**
* Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something
* If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item
*/
index: InteractionModeFunction;
/**
* Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something
* If the options.intersect is false, we find the nearest item and return the items in that dataset
*/
dataset: InteractionModeFunction;
/**
* Point mode returns all elements that hit test based on the event position
* of the event
*/
point: InteractionModeFunction;
/**
* nearest mode returns the element closest to the point
*/
nearest: InteractionModeFunction;
/**
* x mode returns the elements that hit-test at the current x coordinate
*/
x: InteractionModeFunction;
/**
* y mode returns the elements that hit-test at the current y coordinate
*/
y: InteractionModeFunction;
}
export type InteractionMode = keyof InteractionModeMap;
export declare const Interaction: {
modes: InteractionModeMap;
/**
* Helper function to select candidate elements for interaction
*/
evaluateInteractionItems(
chart: Chart,
axis: InteractionAxis,
position: Point,
handler: (element: Element & VisualElement, datasetIndex: number, index: number) => void,
intersect?: boolean
): InteractionItem[];
};
export declare const layouts: {
/**
* Register a box to a chart.
* A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title.
* @param {Chart} chart - the chart to use
* @param {LayoutItem} item - the item to add to be laid out
*/
addBox(chart: Chart, item: LayoutItem): void;
/**
* Remove a layoutItem from a chart
* @param {Chart} chart - the chart to remove the box from
* @param {LayoutItem} layoutItem - the item to remove from the layout
*/
removeBox(chart: Chart, layoutItem: LayoutItem): void;
/**
* Sets (or updates) options on the given `item`.
* @param {Chart} chart - the chart in which the item lives (or will be added to)
* @param {LayoutItem} item - the item to configure with the given options
* @param options - the new item options.
*/
configure(
chart: Chart,
item: LayoutItem,
options: { fullSize?: number; position?: LayoutPosition; weight?: number }
): void;
/**
* Fits boxes of the given chart into the given size by having each box measure itself
* then running a fitting algorithm
* @param {Chart} chart - the chart
* @param {number} width - the width to fit into
* @param {number} height - the height to fit into
*/
update(chart: Chart, width: number, height: number): void;
};
export interface Plugin<TType extends ChartType = ChartType, O = AnyObject> extends ExtendedPlugin<TType, O> {
id: string;
/**
* The events option defines the browser events that the plugin should listen.
* @default ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove']
*/
events?: (keyof HTMLElementEventMap)[]
/**
* @desc Called when plugin is installed for this chart instance. This hook is also invoked for disabled plugins (options === false).
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
* @since 3.0.0
*/
install?(chart: Chart<TType>, args: EmptyObject, options: O): void;
/**
* @desc Called when a plugin is starting. This happens when chart is created or plugin is enabled.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
* @since 3.0.0
*/
start?(chart: Chart<TType>, args: EmptyObject, options: O): void;
/**
* @desc Called when a plugin stopping. This happens when chart is destroyed or plugin is disabled.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
* @since 3.0.0
*/
stop?(chart: Chart<TType>, args: EmptyObject, options: O): void;
/**
* @desc Called before initializing `chart`.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
*/
beforeInit?(chart: Chart<TType>, args: EmptyObject, options: O): void;
/**
* @desc Called after `chart` has been initialized and before the first update.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
*/
afterInit?(chart: Chart<TType>, args: EmptyObject, options: O): void;
/**
* @desc Called before updating `chart`. If any plugin returns `false`, the update
* is cancelled (and thus subsequent render(s)) until another `update` is triggered.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {UpdateMode} args.mode - The update mode
* @param {object} options - The plugin options.
* @returns {boolean} `false` to cancel the chart update.
*/
beforeUpdate?(chart: Chart<TType>, args: { mode: UpdateMode, cancelable: true }, options: O): boolean | void;
/**
* @desc Called after `chart` has been updated and before rendering. Note that this
* hook will not be called if the chart update has been previously cancelled.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {UpdateMode} args.mode - The update mode
* @param {object} options - The plugin options.
*/
afterUpdate?(chart: Chart<TType>, args: { mode: UpdateMode }, options: O): void;
/**
* @desc Called during the update process, before any chart elements have been created.
* This can be used for data decimation by changing the data array inside a dataset.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
*/
beforeElementsUpdate?(chart: Chart<TType>, args: EmptyObject, options: O): void;
/**
* @desc Called during chart reset
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
* @since version 3.0.0
*/
reset?(chart: Chart<TType>, args: EmptyObject, options: O): void;
/**
* @desc Called before updating the `chart` datasets. If any plugin returns `false`,
* the datasets update is cancelled until another `update` is triggered.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {UpdateMode} args.mode - The update mode.
* @param {object} options - The plugin options.
* @returns {boolean} false to cancel the datasets update.
* @since version 2.1.5
*/
beforeDatasetsUpdate?(chart: Chart<TType>, args: { mode: UpdateMode }, options: O): boolean | void;
/**
* @desc Called after the `chart` datasets have been updated. Note that this hook
* will not be called if the datasets update has been previously cancelled.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {UpdateMode} args.mode - The update mode.
* @param {object} options - The plugin options.
* @since version 2.1.5
*/
afterDatasetsUpdate?(chart: Chart<TType>, args: { mode: UpdateMode, cancelable: true }, options: O): void;
/**
* @desc Called before updating the `chart` dataset at the given `args.index`. If any plugin
* returns `false`, the datasets update is cancelled until another `update` is triggered.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {number} args.index - The dataset index.
* @param {object} args.meta - The dataset metadata.
* @param {UpdateMode} args.mode - The update mode.
* @param {object} options - The plugin options.
* @returns {boolean} `false` to cancel the chart datasets drawing.
*/
beforeDatasetUpdate?(chart: Chart<TType>, args: { index: number; meta: ChartMeta, mode: UpdateMode, cancelable: true }, options: O): boolean | void;
/**
* @desc Called after the `chart` datasets at the given `args.index` has been updated. Note
* that this hook will not be called if the datasets update has been previously cancelled.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {number} args.index - The dataset index.
* @param {object} args.meta - The dataset metadata.
* @param {UpdateMode} args.mode - The update mode.
* @param {object} options - The plugin options.
*/
afterDatasetUpdate?(chart: Chart<TType>, args: { index: number; meta: ChartMeta, mode: UpdateMode, cancelable: false }, options: O): void;
/**
* @desc Called before laying out `chart`. If any plugin returns `false`,
* the layout update is cancelled until another `update` is triggered.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
* @returns {boolean} `false` to cancel the chart layout.
*/
beforeLayout?(chart: Chart<TType>, args: { cancelable: true }, options: O): boolean | void;
/**
* @desc Called before scale data limits are calculated. This hook is called separately for each scale in the chart.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {Scale} args.scale - The scale.
* @param {object} options - The plugin options.
*/
beforeDataLimits?(chart: Chart<TType>, args: { scale: Scale }, options: O): void;
/**
* @desc Called after scale data limits are calculated. This hook is called separately for each scale in the chart.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {Scale} args.scale - The scale.
* @param {object} options - The plugin options.
*/
afterDataLimits?(chart: Chart<TType>, args: { scale: Scale }, options: O): void;
/**
* @desc Called before scale builds its ticks. This hook is called separately for each scale in the chart.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {Scale} args.scale - The scale.
* @param {object} options - The plugin options.
*/
beforeBuildTicks?(chart: Chart<TType>, args: { scale: Scale }, options: O): void;
/**
* @desc Called after scale has build its ticks. This hook is called separately for each scale in the chart.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {Scale} args.scale - The scale.
* @param {object} options - The plugin options.
*/
afterBuildTicks?(chart: Chart<TType>, args: { scale: Scale }, options: O): void;
/**
* @desc Called after the `chart` has been laid out. Note that this hook will not
* be called if the layout update has been previously cancelled.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
*/
afterLayout?(chart: Chart<TType>, args: EmptyObject, options: O): void;
/**
* @desc Called before rendering `chart`. If any plugin returns `false`,
* the rendering is cancelled until another `render` is triggered.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
* @returns {boolean} `false` to cancel the chart rendering.
*/
beforeRender?(chart: Chart<TType>, args: { cancelable: true }, options: O): boolean | void;
/**
* @desc Called after the `chart` has been fully rendered (and animation completed). Note
* that this hook will not be called if the rendering has been previously cancelled.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
*/
afterRender?(chart: Chart<TType>, args: EmptyObject, options: O): void;
/**
* @desc Called before drawing `chart` at every animation frame. If any plugin returns `false`,
* the frame drawing is cancelled untilanother `render` is triggered.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
* @returns {boolean} `false` to cancel the chart drawing.
*/
beforeDraw?(chart: Chart<TType>, args: { cancelable: true }, options: O): boolean | void;
/**
* @desc Called after the `chart` has been drawn. Note that this hook will not be called
* if the drawing has been previously cancelled.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
*/
afterDraw?(chart: Chart<TType>, args: EmptyObject, options: O): void;
/**
* @desc Called before drawing the `chart` datasets. If any plugin returns `false`,
* the datasets drawing is cancelled until another `render` is triggered.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
* @returns {boolean} `false` to cancel the chart datasets drawing.
*/
beforeDatasetsDraw?(chart: Chart<TType>, args: { cancelable: true }, options: O): boolean | void;
/**
* @desc Called after the `chart` datasets have been drawn. Note that this hook
* will not be called if the datasets drawing has been previously cancelled.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
*/
afterDatasetsDraw?(chart: Chart<TType>, args: EmptyObject, options: O, cancelable: false): void;
/**
* @desc Called before drawing the `chart` dataset at the given `args.index` (datasets
* are drawn in the reverse order). If any plugin returns `false`, the datasets drawing
* is cancelled until another `render` is triggered.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {number} args.index - The dataset index.
* @param {object} args.meta - The dataset metadata.
* @param {object} options - The plugin options.
* @returns {boolean} `false` to cancel the chart datasets drawing.
*/
beforeDatasetDraw?(chart: Chart<TType>, args: { index: number; meta: ChartMeta }, options: O): boolean | void;
/**
* @desc Called after the `chart` datasets at the given `args.index` have been drawn
* (datasets are drawn in the reverse order). Note that this hook will not be called
* if the datasets drawing has been previously cancelled.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {number} args.index - The dataset index.
* @param {object} args.meta - The dataset metadata.
* @param {object} options - The plugin options.
*/
afterDatasetDraw?(chart: Chart<TType>, args: { index: number; meta: ChartMeta }, options: O): void;
/**
* @desc Called before processing the specified `event`. If any plugin returns `false`,
* the event will be discarded.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {ChartEvent} args.event - The event object.
* @param {boolean} args.replay - True if this event is replayed from `Chart.update`
* @param {boolean} args.inChartArea - The event position is inside chartArea
* @param {boolean} [args.changed] - Set to true if the plugin needs a render. Should only be changed to true, because this args object is passed through all plugins.
* @param {object} options - The plugin options.
*/
beforeEvent?(chart: Chart<TType>, args: { event: ChartEvent, replay: boolean, changed?: boolean; cancelable: true, inChartArea: boolean }, options: O): boolean | void;
/**
* @desc Called after the `event` has been consumed. Note that this hook
* will not be called if the `event` has been previously discarded.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {ChartEvent} args.event - The event object.
* @param {boolean} args.replay - True if this event is replayed from `Chart.update`
* @param {boolean} args.inChartArea - The event position is inside chartArea
* @param {boolean} [args.changed] - Set to true if the plugin needs a render. Should only be changed to true, because this args object is passed through all plugins.
* @param {object} options - The plugin options.
*/
afterEvent?(chart: Chart<TType>, args: { event: ChartEvent, replay: boolean, changed?: boolean, cancelable: false, inChartArea: boolean }, options: O): void;
/**
* @desc Called after the chart as been resized.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {number} args.size - The new canvas display size (eq. canvas.style width & height).
* @param {object} options - The plugin options.
*/
resize?(chart: Chart<TType>, args: { size: { width: number, height: number } }, options: O): void;
/**
* Called before the chart is being destroyed.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
*/
beforeDestroy?(chart: Chart<TType>, args: EmptyObject, options: O): void;
/**
* Called after the chart has been destroyed.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
*/
afterDestroy?(chart: Chart<TType>, args: EmptyObject, options: O): void;
/**
* Called after chart is destroyed on all plugins that were installed for that chart. This hook is also invoked for disabled plugins (options === false).
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {object} options - The plugin options.
* @since 3.0.0
*/
uninstall?(chart: Chart<TType>, args: EmptyObject, options: O): void;
/**
* Default options used in the plugin
*/
defaults?: Partial<O>;
}
export declare type ChartComponentLike = ChartComponent | ChartComponent[] | { [key: string]: ChartComponent } | Plugin | Plugin[];
/**
* Please use the module's default export which provides a singleton instance
* Note: class is exported for typedoc
*/
export interface Registry {
readonly controllers: TypedRegistry<DatasetController>;
readonly elements: TypedRegistry<Element>;
readonly plugins: TypedRegistry<Plugin>;
readonly scales: TypedRegistry<Scale>;
add(...args: ChartComponentLike[]): void;
remove(...args: ChartComponentLike[]): void;
addControllers(...args: ChartComponentLike[]): void;
addElements(...args: ChartComponentLike[]): void;
addPlugins(...args: ChartComponentLike[]): void;
addScales(...args: ChartComponentLike[]): void;
getController(id: string): DatasetController | undefined;
getElement(id: string): Element | undefined;
getPlugin(id: string): Plugin | undefined;
getScale(id: string): Scale | undefined;
}
export declare const registry: Registry;
export interface Tick {
value: number;
label?: string | string[];
major?: boolean;
}
export interface CoreScaleOptions {
/**
* Controls the axis global visibility (visible when true, hidden when false). When display: 'auto', the axis is visible only if at least one associated dataset is visible.
* @default true
*/
display: boolean | 'auto';
/**
* Align pixel values to device pixels
*/
alignToPixels: boolean;
/**
* Background color of the scale area.
*/
backgroundColor: Color;
/**
* Reverse the scale.
* @default false
*/
reverse: boolean;
/**
* Clip the dataset drawing against the size of the scale instead of chart area.
* @default true
*/
clip: boolean;
/**
* The weight used to sort the axis. Higher weights are further away from the chart area.
* @default true
*/
weight: number;
/**
* User defined minimum value for the scale, overrides minimum value from data.
*/
min: unknown;
/**
* User defined maximum value for the scale, overrides maximum value from data.
*/
max: unknown;
/**
* Adjustment used when calculating the maximum data value.
*/
suggestedMin: unknown;
/**
* Adjustment used when calculating the minimum data value.
*/
suggestedMax: unknown;
/**
* Callback called before the update process starts.
*/
beforeUpdate(axis: Scale): void;
/**
* Callback that runs before dimensions are set.
*/
beforeSetDimensions(axis: Scale): void;
/**
* Callback that runs after dimensions are set.
*/
afterSetDimensions(axis: Scale): void;
/**
* Callback that runs before data limits are determined.
*/
beforeDataLimits(axis: Scale): void;
/**
* Callback that runs after data limits are determined.
*/
afterDataLimits(axis: Scale): void;
/**
* Callback that runs before ticks are created.
*/
beforeBuildTicks(axis: Scale): void;
/**
* Callback that runs after ticks are created. Useful for filtering ticks.
*/
afterBuildTicks(axis: Scale): void;
/**
* Callback that runs before ticks are converted into strings.
*/
beforeTickToLabelConversion(axis: Scale): void;
/**
* Callback that runs after ticks are converted into strings.
*/
afterTickToLabelConversion(axis: Scale): void;
/**
* Callback that runs before tick rotation is determined.
*/
beforeCalculateLabelRotation(axis: Scale): void;
/**
* Callback that runs after tick rotation is determined.
*/
afterCalculateLabelRotation(axis: Scale): void;
/**
* Callback that runs before the scale fits to the canvas.
*/
beforeFit(axis: Scale): void;
/**
* Callback that runs after the scale fits to the canvas.
*/
afterFit(axis: Scale): void;
/**
* Callback that runs at the end of the update process.
*/
afterUpdate(axis: Scale): void;
}
export interface Scale<O extends CoreScaleOptions = CoreScaleOptions> extends Element<unknown, O>, LayoutItem {
readonly id: string;
readonly type: string;
readonly ctx: CanvasRenderingContext2D;
readonly chart: Chart;
maxWidth: number;
maxHeight: number;
paddingTop: number;
paddingBottom: number;
paddingLeft: number;
paddingRight: number;
axis: string;
labelRotation: number;
min: number;
max: number;
ticks: Tick[];
getMatchingVisibleMetas(type?: string): ChartMeta[];
drawTitle(chartArea: ChartArea): void;
drawLabels(chartArea: ChartArea): void;
drawGrid(chartArea: ChartArea): void;
/**
* @param {number} pixel
* @return {number}
*/
getDecimalForPixel(pixel: number): number;
/**
* Utility for getting the pixel location of a percentage of scale
* The coordinate (0, 0) is at the upper-left corner of the canvas
* @param {number} decimal
* @return {number}
*/
getPixelForDecimal(decimal: number): number;
/**
* Returns the location of the tick at the given index
* The coordinate (0, 0) is at the upper-left corner of the canvas
* @param {number} index
* @return {number}
*/
getPixelForTick(index: number): number;
/**
* Used to get the label to display in the tooltip for the given value
* @param {*} value
* @return {string}
*/
getLabelForValue(value: number): string;
/**
* Returns the grid line width at given value
*/
getLineWidthForValue(value: number): number;
/**
* Returns the location of the given data point. Value can either be an index or a numerical value
* The coordinate (0, 0) is at the upper-left corner of the canvas
* @param {*} value
* @param {number} [index]
* @return {number}
*/
getPixelForValue(value: number, index?: number): number;
/**
* Used to get the data value from a given pixel. This is the inverse of getPixelForValue
* The coordinate (0, 0) is at the upper-left corner of the canvas
* @param {number} pixel
* @return {*}
*/
getValueForPixel(pixel: number): number | undefined;
getBaseValue(): number;
/**
* Returns the pixel for the minimum chart value
* The coordinate (0, 0) is at the upper-left corner of the canvas
* @return {number}
*/
getBasePixel(): number;
init(options: O): void;
parse(raw: unknown, index?: number): unknown;
getUserBounds(): { min: number; max: number; minDefined: boolean; maxDefined: boolean };
getMinMax(canStack: boolean): { min: number; max: number };
getTicks(): Tick[];
getLabels(): string[];
getLabelItems(chartArea?: ChartArea): LabelItem[];
beforeUpdate(): void;
configure(): void;
afterUpdate(): void;
beforeSetDimensions(): void;
setDimensions(): void;
afterSetDimensions(): void;
beforeDataLimits(): void;
determineDataLimits(): void;
afterDataLimits(): void;
beforeBuildTicks(): void;
buildTicks(): Tick[];
afterBuildTicks(): void;
beforeTickToLabelConversion(): void;
generateTickLabels(ticks: Tick[]): void;
afterTickToLabelConversion(): void;
beforeCalculateLabelRotation(): void;
calculateLabelRotation(): void;
afterCalculateLabelRotation(): void;
beforeFit(): void;
fit(): void;
afterFit(): void;
isFullSize(): boolean;
}
export declare class Scale {
constructor(cfg: {id: string, type: string, ctx: CanvasRenderingContext2D, chart: Chart});
}
export interface ScriptableScaleContext {
chart: Chart;
scale: Scale;
index: number;
tick: Tick;
}
export interface ScriptableScalePointLabelContext {
chart: Chart;
scale: Scale;
index: number;
label: string;
type: string;
}
export interface RenderTextOpts {
/**
* The fill color of the text. If unset, the existing
* fillStyle property of the canvas is unchanged.
*/
color?: Color;
/**
* The width of the strikethrough / underline
* @default 2
*/
decorationWidth?: number;
/**
* The max width of the text in pixels
*/
maxWidth?: number;
/**
* A rotation to be applied to the canvas
* This is applied after the translation is applied
*/
rotation?: number;
/**
* Apply a strikethrough effect to the text
*/
strikethrough?: boolean;
/**
* The color of the text stroke. If unset, the existing
* strokeStyle property of the context is unchanged
*/
strokeColor?: Color;
/**
* The text stroke width. If unset, the existing
* lineWidth property of the context is unchanged
*/
strokeWidth?: number;
/**
* The text alignment to use. If unset, the existing
* textAlign property of the context is unchanged
*/
textAlign?: CanvasTextAlign;
/**
* The text baseline to use. If unset, the existing
* textBaseline property of the context is unchanged
*/
textBaseline?: CanvasTextBaseline;
/**
* If specified, a translation to apply to the context
*/
translation?: [number, number];
/**
* Underline the text
*/
underline?: boolean;
/**
* Dimensions for drawing the label backdrop
*/
backdrop?: BackdropOptions;
}
export interface BackdropOptions {
/**
* Left position of backdrop as pixel
*/
left: number;
/**
* Top position of backdrop as pixel
*/
top: number;
/**
* Width of backdrop in pixels
*/
width: number;
/**
* Height of backdrop in pixels
*/
height: number;
/**
* Color of label backdrops.
*/
color: Scriptable<Color, ScriptableScaleContext>;
}
export interface LabelItem {
label: string | string[];
font: CanvasFontSpec;
textOffset: number;
options: RenderTextOpts;
}
export declare const Ticks: {
formatters: {
/**
* Formatter for value labels
* @param value the value to display
* @return {string|string[]} the label to display
*/
values(value: unknown): string | string[];
/**
* Formatter for numeric ticks
* @param tickValue the value to be formatted
* @param index the position of the tickValue parameter in the ticks array
* @param ticks the list of ticks being converted
* @return string representation of the tickValue parameter
*/
numeric(this: Scale, tickValue: number, index: number, ticks: { value: number }[]): string;
/**
* Formatter for logarithmic ticks
* @param tickValue the value to be formatted
* @param index the position of the tickValue parameter in the ticks array
* @param ticks the list of ticks being converted
* @return string representation of the tickValue parameter
*/
logarithmic(this: Scale, tickValue: number, index: number, ticks: { value: number }[]): string;
};
};
export interface TypedRegistry<T> {
/**
* @param {ChartComponent} item
* @returns {string} The scope where items defaults were registered to.
*/
register(item: ChartComponent): string;
get(id: string): T | undefined;
unregister(item: ChartComponent): void;
}
export interface ChartEvent {
type:
| 'contextmenu'
| 'mouseenter'
| 'mousedown'
| 'mousemove'
| 'mouseup'
| 'mouseout'
| 'click'
| 'dblclick'
| 'keydown'
| 'keypress'
| 'keyup'
| 'resize';
native: Event | null;
x: number | null;
y: number | null;
}
export interface ChartComponent {
id: string;
defaults?: AnyObject;
defaultRoutes?: { [property: string]: string };
beforeRegister?(): void;
afterRegister?(): void;
beforeUnregister?(): void;
afterUnregister?(): void;
}
export type InteractionAxis = 'x' | 'y' | 'xy' | 'r';
export interface CoreInteractionOptions {
/**
* Sets which elements appear in the tooltip. See Interaction Modes for details.
* @default 'nearest'
*/
mode: InteractionMode;
/**
* if true, the hover mode only applies when the mouse position intersects an item on the chart.
* @default true
*/
intersect: boolean;
/**
* Defines which directions are used in calculating distances. Defaults to 'x' for 'index' mode and 'xy' in dataset and 'nearest' modes.
*/
axis: InteractionAxis;
/**
* if true, the invisible points that are outside of the chart area will also be included when evaluating interactions.
* @default false
*/
includeInvisible: boolean;
}
export interface CoreChartOptions<TType extends ChartType> extends ParsingOptions, AnimationOptions<TType> {
datasets: {
[key in ChartType]: ChartTypeRegistry[key]['datasetOptions']
}
/**
* The base axis of the chart. 'x' for vertical charts and 'y' for horizontal charts.
* @default 'x'
*/
indexAxis: 'x' | 'y';
/**
* How to clip relative to chartArea. Positive value allows overflow, negative value clips that many pixels inside chartArea. 0 = clip at chartArea. Clipping can also be configured per side: `clip: {left: 5, top: false, right: -2, bottom: 0}`
*/
clip: number | ChartArea | false;
/**
* base color
* @see Defaults.color
*/
color: Scriptable<Color, ScriptableContext<TType>>;
/**
* base background color
* @see Defaults.backgroundColor
*/
backgroundColor: ScriptableAndArray<Color, ScriptableContext<TType>>;
/**
* base hover background color
* @see Defaults.hoverBackgroundColor
*/
hoverBackgroundColor: ScriptableAndArray<Color, ScriptableContext<TType>>;
/**
* base border color
* @see Defaults.borderColor
*/
borderColor: ScriptableAndArray<Color, ScriptableContext<TType>>;
/**
* base hover border color
* @see Defaults.hoverBorderColor
*/
hoverBorderColor: ScriptableAndArray<Color, ScriptableContext<TType>>;
/**
* base font
* @see Defaults.font
*/
font: Partial<FontSpec>;
/**
* Resizes the chart canvas when its container does (important note...).
* @default true
*/
responsive: boolean;
/**
* Maintain the original canvas aspect ratio (width / height) when resizing. For this option to work properly the chart must be in its own dedicated container.
* @default true
*/
maintainAspectRatio: boolean;
/**
* Delay the resize update by give amount of milliseconds. This can ease the resize process by debouncing update of the elements.
* @default 0
*/
resizeDelay: number;
/**
* Canvas aspect ratio (i.e. width / height, a value of 1 representing a square canvas). Note that this option is ignored if the height is explicitly defined either as attribute or via the style.
* @default 2
*/
aspectRatio: number;
/**
* Locale used for number formatting (using `Intl.NumberFormat`).
* @default user's browser setting
*/
locale: string;
/**
* Called when a resize occurs. Gets passed two arguments: the chart instance and the new size.
*/
onResize(chart: Chart, size: { width: number; height: number }): void;
/**
* Override the window's default devicePixelRatio.
* @default window.devicePixelRatio
*/
devicePixelRatio: number;
interaction: CoreInteractionOptions;
hover: CoreInteractionOptions;
/**
* The events option defines the browser events that the chart should listen to for tooltips and hovering.
* @default ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove']
*/
events: (keyof HTMLElementEventMap)[]
/**
* Called when any of the events fire. Passed the event, an array of active elements (bars, points, etc), and the chart.
*/
onHover(event: ChartEvent, elements: ActiveElement[], chart: Chart): void;
/**
* Called if the event is of type 'mouseup' or 'click'. Passed the event, an array of active elements, and the chart.
*/
onClick(event: ChartEvent, elements: ActiveElement[], chart: Chart): void;
layout: Partial<{
autoPadding: boolean;
padding: Scriptable<Padding, ScriptableContext<TType>>;
}>;
}
export type AnimationSpec<TType extends ChartType> = {
/**
* The number of milliseconds an animation takes.
* @default 1000
*/
duration?: Scriptable<number, ScriptableContext<TType>>;
/**
* Easing function to use
* @default 'easeOutQuart'
*/
easing?: Scriptable<EasingFunction, ScriptableContext<TType>>;
/**
* Delay before starting the animations.
* @default 0
*/
delay?: Scriptable<number, ScriptableContext<TType>>;
/**
* If set to true, the animations loop endlessly.
* @default false
*/
loop?: Scriptable<boolean, ScriptableContext<TType>>;
}
export type AnimationsSpec<TType extends ChartType> = {
[name: string]: false | AnimationSpec<TType> & {
properties: string[];
/**
* Type of property, determines the interpolator used. Possible values: 'number', 'color' and 'boolean'. Only really needed for 'color', because typeof does not get that right.
*/
type: 'color' | 'number' | 'boolean';
fn: <T>(from: T, to: T, factor: number) => T;
/**
* Start value for the animation. Current value is used when undefined
*/
from: Scriptable<Color | number | boolean, ScriptableContext<TType>>;
/**
*
*/
to: Scriptable<Color | number | boolean, ScriptableContext<TType>>;
}
}
export type TransitionSpec<TType extends ChartType> = {
animation: AnimationSpec<TType>;
animations: AnimationsSpec<TType>;
}
export type TransitionsSpec<TType extends ChartType> = {
[mode: string]: TransitionSpec<TType>
}
export type AnimationOptions<TType extends ChartType> = {
animation: false | AnimationSpec<TType> & {
/**
* Callback called on each step of an animation.
*/
onProgress?: (this: Chart, event: AnimationEvent) => void;
/**
* Callback called when all animations are completed.
*/
onComplete?: (this: Chart, event: AnimationEvent) => void;
};
animations: AnimationsSpec<TType>;
transitions: TransitionsSpec<TType>;
};
export interface FontSpec {
/**
* Default font family for all text, follows CSS font-family options.
* @default "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"
*/
family: string;
/**
* Default font size (in px) for text. Does not apply to radialLinear scale point labels.
* @default 12
*/
size: number;
/**
* Default font style. Does not apply to tooltip title or footer. Does not apply to chart title. Follows CSS font-style options (i.e. normal, italic, oblique, initial, inherit)
* @default 'normal'
*/
style: 'normal' | 'italic' | 'oblique' | 'initial' | 'inherit';
/**
* Default font weight (boldness). (see MDN).
*/
weight: 'normal' | 'bold' | 'lighter' | 'bolder' | number | null;
/**
* Height of an individual line of text (see MDN).
* @default 1.2
*/
lineHeight: number | string;
}
export interface CanvasFontSpec extends FontSpec {
string: string;
}
export type TextAlign = 'left' | 'center' | 'right';
export type Align = 'start' | 'center' | 'end';
export interface VisualElement {
draw(ctx: CanvasRenderingContext2D, area?: ChartArea): void;
inRange(mouseX: number, mouseY: number, useFinalPosition?: boolean): boolean;
inXRange(mouseX: number, useFinalPosition?: boolean): boolean;
inYRange(mouseY: number, useFinalPosition?: boolean): boolean;
getCenterPoint(useFinalPosition?: boolean): Point;
getRange?(axis: 'x' | 'y'): number;
}
export interface CommonElementOptions {
borderWidth: number;
borderColor: Color;
backgroundColor: Color;
}
export interface CommonHoverOptions {
hoverBorderWidth: number;
hoverBorderColor: Color;
hoverBackgroundColor: Color;
}
export interface Segment {
start: number;
end: number;
loop: boolean;
}
export interface ArcBorderRadius {
outerStart: number;
outerEnd: number;
innerStart: number;
innerEnd: number;
}
export interface ArcOptions extends CommonElementOptions {
/**
* If true, Arc can take up 100% of a circular graph without any visual split or cut. This option doesn't support borderRadius and borderJoinStyle miter
* @default true
*/
selfJoin: boolean;
/**
* Arc stroke alignment.
*/
borderAlign: 'center' | 'inner';
/**
* Line dash. See MDN.
* @default []
*/
borderDash: number[];
/**
* Line dash offset. See MDN.
* @default 0.0
*/
borderDashOffset: number;
/**
* Line join style. See MDN. Default is 'round' when `borderAlign` is 'inner', else 'bevel'.
*/
borderJoinStyle: CanvasLineJoin;
/**
* Sets the border radius for arcs
* @default 0
*/
borderRadius: number | ArcBorderRadius;
/**
* Arc offset (in pixels).
*/
offset: number;
/**
* If false, Arc will be flat.
* @default true
*/
circular: boolean;
/**
* Spacing between arcs
*/
spacing: number
}
export interface ArcHoverOptions extends CommonHoverOptions {
hoverBorderDash: number[];
hoverBorderDashOffset: number;
hoverOffset: number;
}
export interface LineProps {
points: Point[]
}
export interface LineOptions extends CommonElementOptions {
/**
* Line cap style. See MDN.
* @default 'butt'
*/
borderCapStyle: CanvasLineCap;
/**
* Line dash. See MDN.
* @default []
*/
borderDash: number[];
/**
* Line dash offset. See MDN.
* @default 0.0
*/
borderDashOffset: number;
/**
* Line join style. See MDN.
* @default 'miter'
*/
borderJoinStyle: CanvasLineJoin;
/**
* true to keep Bézier control inside the chart, false for no restriction.
* @default true
*/
capBezierPoints: boolean;
/**
* Interpolation mode to apply.
* @default 'default'
*/
cubicInterpolationMode: 'default' | 'monotone';
/**
* Bézier curve tension (0 for no Bézier curves).
* @default 0
*/
tension: number;
/**
* true to show the line as a stepped line (tension will be ignored).
* @default false
*/
stepped: 'before' | 'after' | 'middle' | boolean;
/**
* Both line and radar charts support a fill option on the dataset object which can be used to create area between two datasets or a dataset and a boundary, i.e. the scale origin, start or end
*/
fill: FillTarget | ComplexFillTarget;
/**
* If true, lines will be drawn between points with no or null data. If false, points with NaN data will create a break in the line. Can also be a number specifying the maximum gap length to span. The unit of the value depends on the scale used.
*/
spanGaps: boolean | number;
segment: {
backgroundColor: Scriptable<Color|undefined, ScriptableLineSegmentContext>,
borderColor: Scriptable<Color|undefined, ScriptableLineSegmentContext>,
borderCapStyle: Scriptable<CanvasLineCap|undefined, ScriptableLineSegmentContext>;
borderDash: Scriptable<number[]|undefined, ScriptableLineSegmentContext>;
borderDashOffset: Scriptable<number|undefined, ScriptableLineSegmentContext>;
borderJoinStyle: Scriptable<CanvasLineJoin|undefined, ScriptableLineSegmentContext>;
borderWidth: Scriptable<number|undefined, ScriptableLineSegmentContext>;
};
}
export interface LineHoverOptions extends CommonHoverOptions {
hoverBorderCapStyle: CanvasLineCap;
hoverBorderDash: number[];
hoverBorderDashOffset: number;
hoverBorderJoinStyle: CanvasLineJoin;
}
export interface LineElement<T extends LineProps = LineProps, O extends LineOptions = LineOptions>
extends Element<T, O>,
VisualElement {
updateControlPoints(chartArea: ChartArea, indexAxis?: 'x' | 'y'): void;
points: Point[];
readonly segments: Segment[];
first(): Point | false;
last(): Point | false;
interpolate(point: Point, property: 'x' | 'y'): undefined | Point | Point[];
pathSegment(ctx: CanvasRenderingContext2D, segment: Segment, params: AnyObject): undefined | boolean;
path(ctx: CanvasRenderingContext2D): boolean;
}
export declare const LineElement: ChartComponent & {
prototype: LineElement;
new (cfg: AnyObject): LineElement;
};
export type PointStyle =
| 'circle'
| 'cross'
| 'crossRot'
| 'dash'
| 'line'
| 'rect'
| 'rectRounded'
| 'rectRot'
| 'star'
| 'triangle'
| false
| HTMLImageElement
| HTMLCanvasElement;
export interface PointOptions extends CommonElementOptions {
/**
* Point radius
* @default 3
*/
radius: number;
/**
* Extra radius added to point radius for hit detection.
* @default 1
*/
hitRadius: number;
/**
* Point style
* @default 'circle;
*/
pointStyle: PointStyle;
/**
* Point rotation (in degrees).
* @default 0
*/
rotation: number;
/**
* Draw the active elements over the other elements of the dataset,
* @default true
*/
drawActiveElementsOnTop: boolean;
}
export interface PointHoverOptions extends CommonHoverOptions {
/**
* Point radius when hovered.
* @default 4
*/
hoverRadius: number;
}
export interface PointPrefixedOptions {
/**
* The fill color for points.
*/
pointBackgroundColor: Color;
/**
* The border color for points.
*/
pointBorderColor: Color;
/**
* The width of the point border in pixels.
*/
pointBorderWidth: number;
/**
* The pixel size of the non-displayed point that reacts to mouse events.
*/
pointHitRadius: number;
/**
* The radius of the point shape. If set to 0, the point is not rendered.
*/
pointRadius: number;
/**
* The rotation of the point in degrees.
*/
pointRotation: number;
/**
* Style of the point.
*/
pointStyle: PointStyle;
}
export interface PointPrefixedHoverOptions {
/**
* Point background color when hovered.
*/
pointHoverBackgroundColor: Color;
/**
* Point border color when hovered.
*/
pointHoverBorderColor: Color;
/**
* Border width of point when hovered.
*/
pointHoverBorderWidth: number;
/**
* The radius of the point when hovered.
*/
pointHoverRadius: number;
}
export interface BarProps extends Point {
base: number;
horizontal: boolean;
width: number;
height: number;
}
export interface BarOptions extends Omit<CommonElementOptions, 'borderWidth'> {
/**
* The base value for the bar in data units along the value axis.
*/
base: number;
/**
* Skipped (excluded) border: 'start', 'end', 'left', 'right', 'bottom', 'top', 'middle', false (none) or true (all).
* @default 'start'
*/
borderSkipped: 'start' | 'end' | 'left' | 'right' | 'bottom' | 'top' | 'middle' | boolean;
/**
* Border radius
* @default 0
*/
borderRadius: number | BorderRadius;
/**
* Amount to inflate the rectangle(s). This can be used to hide artifacts between bars.
* Unit is pixels. 'auto' translates to 0.33 pixels when barPercentage * categoryPercentage is 1, else 0.
* @default 'auto'
*/
inflateAmount: number | 'auto';
/**
* Width of the border, number for all sides, object to specify width for each side specifically
* @default 0
*/
borderWidth: number | { top?: number, right?: number, bottom?: number, left?: number };
}
export interface BorderRadius {
topLeft: number;
topRight: number;
bottomLeft: number;
bottomRight: number;
}
export interface BarHoverOptions extends CommonHoverOptions {
hoverBorderRadius: number | BorderRadius;
}
export interface BarElement<
T extends BarProps = BarProps,
O extends BarOptions = BarOptions
> extends Element<T, O>, VisualElement {}
export declare const BarElement: ChartComponent & {
prototype: BarElement;
new (cfg: AnyObject): BarElement;
};
export interface ElementOptionsByType<TType extends ChartType> {
arc: ScriptableAndArrayOptions<ArcOptions & ArcHoverOptions, ScriptableContext<TType>>;
bar: ScriptableAndArrayOptions<BarOptions & BarHoverOptions, ScriptableContext<TType>>;
line: ScriptableAndArrayOptions<LineOptions & LineHoverOptions, ScriptableContext<TType>>;
point: ScriptableAndArrayOptions<PointOptions & PointHoverOptions, ScriptableContext<TType>>;
}
export type ElementChartOptions<TType extends ChartType = ChartType> = {
elements: ElementOptionsByType<TType>
};
export declare class BasePlatform {
/**
* Called at chart construction time, returns a context2d instance implementing
* the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}.
* @param {HTMLCanvasElement} canvas - The canvas from which to acquire context (platform specific)
* @param options - The chart options
*/
acquireContext(
canvas: HTMLCanvasElement,
options?: CanvasRenderingContext2DSettings
): CanvasRenderingContext2D | null;
/**
* Called at chart destruction time, releases any resources associated to the context
* previously returned by the acquireContext() method.
* @param {CanvasRenderingContext2D} context - The context2d instance
* @returns {boolean} true if the method succeeded, else false
*/
releaseContext(context: CanvasRenderingContext2D): boolean;
/**
* Registers the specified listener on the given chart.
* @param {Chart} chart - Chart from which to listen for event
* @param {string} type - The ({@link ChartEvent}) type to listen for
* @param listener - Receives a notification (an object that implements
* the {@link ChartEvent} interface) when an event of the specified type occurs.
*/
addEventListener(chart: Chart, type: string, listener: (e: ChartEvent) => void): void;
/**
* Removes the specified listener previously registered with addEventListener.
* @param {Chart} chart - Chart from which to remove the listener
* @param {string} type - The ({@link ChartEvent}) type to remove
* @param listener - The listener function to remove from the event target.
*/
removeEventListener(chart: Chart, type: string, listener: (e: ChartEvent) => void): void;
/**
* @returns {number} the current devicePixelRatio of the device this platform is connected to.
*/
getDevicePixelRatio(): number;
/**
* @param {HTMLCanvasElement} canvas - The canvas for which to calculate the maximum size
* @param {number} [width] - Parent element's content width
* @param {number} [height] - Parent element's content height
* @param {number} [aspectRatio] - The aspect ratio to maintain
* @returns { width: number, height: number } the maximum size available.
*/
getMaximumSize(canvas: HTMLCanvasElement, width?: number, height?: number, aspectRatio?: number): { width: number, height: number };
/**
* @param {HTMLCanvasElement} canvas
* @returns {boolean} true if the canvas is attached to the platform, false if not.
*/
isAttached(canvas: HTMLCanvasElement): boolean;
/**
* Updates config with platform specific requirements
* @param {ChartConfiguration | ChartConfigurationCustomTypes} config
*/
updateConfig(config: ChartConfiguration | ChartConfigurationCustomTypesPerDataset): void;
}
export declare class BasicPlatform extends BasePlatform {}
export declare class DomPlatform extends BasePlatform {}
export declare const Decimation: Plugin;
export declare const enum DecimationAlgorithm {
lttb = 'lttb',
minmax = 'min-max',
}
interface BaseDecimationOptions {
enabled: boolean;
threshold?: number;
}
interface LttbDecimationOptions extends BaseDecimationOptions {
algorithm: DecimationAlgorithm.lttb | 'lttb';
samples?: number;
}
interface MinMaxDecimationOptions extends BaseDecimationOptions {
algorithm: DecimationAlgorithm.minmax | 'min-max';
}
export type DecimationOptions = LttbDecimationOptions | MinMaxDecimationOptions;
export declare const Filler: Plugin;
export interface FillerOptions {
drawTime: 'beforeDraw' | 'beforeDatasetDraw' | 'beforeDatasetsDraw';
propagate: boolean;
}
export type FillTarget = number | string | { value: number } | 'start' | 'end' | 'origin' | 'stack' | 'shape' | boolean;
export interface ComplexFillTarget {
/**
* The accepted values are the same as the filling mode values, so you may use absolute and relative dataset indexes and/or boundaries.
*/
target: FillTarget;
/**
* If no color is set, the default color will be the background color of the chart.
*/
above: Color;
/**
* Same as the above.
*/
below: Color;
}
export interface FillerControllerDatasetOptions {
/**
* Both line and radar charts support a fill option on the dataset object which can be used to create area between two datasets or a dataset and a boundary, i.e. the scale origin, start or end
*/
fill: FillTarget | ComplexFillTarget;
}
export declare const Legend: Plugin;
export interface LegendItem {
/**
* Label that will be displayed
*/
text: string;
/**
* Border radius of the legend box
* @since 3.1.0
*/
borderRadius?: number | BorderRadius;
/**
* Index of the associated dataset
*/
datasetIndex?: number;
/**
* Index the associated label in the labels array
*/
index?: number
/**
* Fill style of the legend box
*/
fillStyle?: Color;
/**
* Font color for the text
* Defaults to LegendOptions.labels.color
*/
fontColor?: Color;
/**
* If true, this item represents a hidden dataset. Label will be rendered with a strike-through effect
*/
hidden?: boolean;
/**
* For box border.
* @see https://developer.mozilla.org/en/docs/Web/API/CanvasRenderingContext2D/lineCap
*/
lineCap?: CanvasLineCap;
/**
* For box border.
* @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash
*/
lineDash?: number[];
/**
* For box border.
* @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineDashOffset
*/
lineDashOffset?: number;
/**
* For box border.
* @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineJoin
*/
lineJoin?: CanvasLineJoin;
/**
* Width of box border
*/
lineWidth?: number;
/**
* Stroke style of the legend box
*/
strokeStyle?: Color;
/**
* Point style of the legend box (only used if usePointStyle is true)
*/
pointStyle?: PointStyle;
/**
* Rotation of the point in degrees (only used if usePointStyle is true)
*/
rotation?: number;
/**
* Text alignment
*/
textAlign?: TextAlign;
}
export interface LegendElement<TType extends ChartType> extends Element<AnyObject, LegendOptions<TType>>, LayoutItem {
chart: Chart<TType>;
ctx: CanvasRenderingContext2D;
legendItems?: LegendItem[];
options: LegendOptions<TType>;
fit(): void;
}
export interface LegendOptions<TType extends ChartType> {
/**
* Is the legend shown?
* @default true
*/
display: boolean;
/**
* Position of the legend.
* @default 'top'
*/
position: LayoutPosition;
/**
* Alignment of the legend.
* @default 'center'
*/
align: Align;
/**
* Maximum height of the legend, in pixels
*/
maxHeight: number;
/**
* Maximum width of the legend, in pixels
*/
maxWidth: number;
/**
* Marks that this box should take the full width/height of the canvas (moving other boxes). This is unlikely to need to be changed in day-to-day use.
* @default true
*/
fullSize: boolean;
/**
* Legend will show datasets in reverse order.
* @default false
*/
reverse: boolean;
/**
* A callback that is called when a click event is registered on a label item.
*/
onClick(this: LegendElement<TType>, e: ChartEvent, legendItem: LegendItem, legend: LegendElement<TType>): void;
/**
* A callback that is called when a 'mousemove' event is registered on top of a label item
*/
onHover(this: LegendElement<TType>, e: ChartEvent, legendItem: LegendItem, legend: LegendElement<TType>): void;
/**
* A callback that is called when a 'mousemove' event is registered outside of a previously hovered label item.
*/
onLeave(this: LegendElement<TType>, e: ChartEvent, legendItem: LegendItem, legend: LegendElement<TType>): void;
labels: {
/**
* Width of colored box.
* @default 40
*/
boxWidth: number;
/**
* Height of the coloured box.
* @default fontSize
*/
boxHeight: number;
/**
* Color of label
* @see Defaults.color
*/
color: Color;
/**
* Font of label
* @see Defaults.font
*/
font: ScriptableAndScriptableOptions<Partial<FontSpec>, ScriptableChartContext>;
/**
* Padding between rows of colored boxes.
* @default 10
*/
padding: number;
/**
* If usePointStyle is true, the width of the point style used for the legend.
*/
pointStyleWidth: number;
/**
* Generates legend items for each thing in the legend. Default implementation returns the text + styling for the color box. See Legend Item for details.
*/
generateLabels(chart: Chart): LegendItem[];
/**
* Filters legend items out of the legend. Receives 2 parameters, a Legend Item and the chart data
*/
filter(item: LegendItem, data: ChartData): boolean;
/**
* Sorts the legend items
*/
sort(a: LegendItem, b: LegendItem, data: ChartData): number;
/**
* Override point style for the legend. Only applies if usePointStyle is true
*/
pointStyle: PointStyle;
/**
* Text alignment
*/
textAlign?: TextAlign;
/**
* Label style will match corresponding point style (size is based on the minimum value between boxWidth and font.size).
* @default false
*/
usePointStyle: boolean;
/**
* Label borderRadius will match corresponding borderRadius.
* @default false
*/
useBorderRadius: boolean;
/**
* Override the borderRadius to use.
* @default undefined
*/
borderRadius: number;
};
/**
* true for rendering the legends from right to left.
*/
rtl: boolean;
/**
* This will force the text direction 'rtl' or 'ltr' on the canvas for rendering the legend, regardless of the css specified on the canvas
* @default canvas's default
*/
textDirection: string;
title: {
/**
* Is the legend title displayed.
* @default false
*/
display: boolean;
/**
* Color of title
* @see Defaults.color
*/
color: Color;
/**
* see Fonts
*/
font: ScriptableAndScriptableOptions<Partial<FontSpec>, ScriptableChartContext>;
position: 'center' | 'start' | 'end';
padding?: number | ChartArea;
/**
* The string title.
*/
text: string;
};
}
export declare const SubTitle: Plugin;
export declare const Title: Plugin;
export interface TitleOptions {
/**
* Alignment of the title.
* @default 'center'
*/
align: Align;
/**
* Is the title shown?
* @default false
*/
display: boolean;
/**
* Position of title
* @default 'top'
*/
position: 'top' | 'left' | 'bottom' | 'right';
/**
* Color of text
* @see Defaults.color
*/
color: Color;
font: ScriptableAndScriptableOptions<Partial<FontSpec>, ScriptableChartContext>;
/**
* Marks that this box should take the full width/height of the canvas (moving other boxes). If set to `false`, places the box above/beside the
* chart area
* @default true
*/
fullSize: boolean;
/**
* Adds padding above and below the title text if a single number is specified. It is also possible to change top and bottom padding separately.
*/
padding: number | { top: number; bottom: number };
/**
* Title text to display. If specified as an array, text is rendered on multiple lines.
*/
text: string | string[];
}
export type TooltipXAlignment = 'left' | 'center' | 'right';
export type TooltipYAlignment = 'top' | 'center' | 'bottom';
export interface TooltipLabelStyle {
borderColor: Color;
backgroundColor: Color;
/**
* Width of border line
* @since 3.1.0
*/
borderWidth?: number;
/**
* Border dash
* @since 3.1.0
*/
borderDash?: [number, number];
/**
* Border dash offset
* @since 3.1.0
*/
borderDashOffset?: number;
/**
* borderRadius
* @since 3.1.0
*/
borderRadius?: number | BorderRadius;
}
export interface TooltipModel<TType extends ChartType> extends Element<AnyObject, TooltipOptions<TType>> {
readonly chart: Chart<TType>;
// The items that we are rendering in the tooltip. See Tooltip Item Interface section
dataPoints: TooltipItem<TType>[];
// Positioning
xAlign: TooltipXAlignment;
yAlign: TooltipYAlignment;
// X and Y properties are the top left of the tooltip
x: number;
y: number;
width: number;
height: number;
// Where the tooltip points to
caretX: number;
caretY: number;
// Body
// The body lines that need to be rendered
// Each object contains 3 parameters
// before: string[] // lines of text before the line with the color square
// lines: string[]; // lines of text to render as the main item with color square
// after: string[]; // lines of text to render after the main lines
body: { before: string[]; lines: string[]; after: string[] }[];
// lines of text that appear after the title but before the body
beforeBody: string[];
// line of text that appear after the body and before the footer
afterBody: string[];
// Title
// lines of text that form the title
title: string[];
// Footer
// lines of text that form the footer
footer: string[];
// Styles to render for each item in body[]. This is the styling of the squares in the tooltip
labelColors: TooltipLabelStyle[];
labelTextColors: Color[];
labelPointStyles: { pointStyle: PointStyle; rotation: number }[];
// 0 opacity is a hidden tooltip
opacity: number;
// tooltip options
options: TooltipOptions<TType>;
getActiveElements(): ActiveElement[];
setActiveElements(active: ActiveDataPoint[], eventPosition: Point): void;
}
export interface TooltipPosition extends Point {
xAlign?: TooltipXAlignment;
yAlign?: TooltipYAlignment;
}
export type TooltipPositionerFunction<TType extends ChartType> = (
this: TooltipModel<TType>,
items: readonly ActiveElement[],
eventPosition: Point
) => TooltipPosition | false;
export interface TooltipPositionerMap {
average: TooltipPositionerFunction<ChartType>;
nearest: TooltipPositionerFunction<ChartType>;
}
export type TooltipPositioner = keyof TooltipPositionerMap;
export interface Tooltip extends Plugin {
readonly positioners: TooltipPositionerMap;
}
export declare const Tooltip: Tooltip;
export interface TooltipDatasetCallbacks<
TType extends ChartType,
Model = TooltipModel<TType>,
Item = TooltipItem<TType>> {
beforeLabel(this: Model, tooltipItem: Item): string | string[] | void;
label(this: Model, tooltipItem: Item): string | string[] | void;
afterLabel(this: Model, tooltipItem: Item): string | string[] | void;
labelColor(this: Model, tooltipItem: Item): TooltipLabelStyle | void;
labelTextColor(this: Model, tooltipItem: Item): Color | void;
labelPointStyle(this: Model, tooltipItem: Item): { pointStyle: PointStyle; rotation: number } | void;
}
export interface TooltipCallbacks<
TType extends ChartType,
Model = TooltipModel<TType>,
Item = TooltipItem<TType>> extends TooltipDatasetCallbacks<TType, Model, Item> {
beforeTitle(this: Model, tooltipItems: Item[]): string | string[] | void;
title(this: Model, tooltipItems: Item[]): string | string[] | void;
afterTitle(this: Model, tooltipItems: Item[]): string | string[] | void;
beforeBody(this: Model, tooltipItems: Item[]): string | string[] | void;
afterBody(this: Model, tooltipItems: Item[]): string | string[] | void;
beforeLabel(this: Model, tooltipItem: Item): string | string[] | void;
label(this: Model, tooltipItem: Item): string | string[] | void;
afterLabel(this: Model, tooltipItem: Item): string | string[] | void;
labelColor(this: Model, tooltipItem: Item): TooltipLabelStyle | void;
labelTextColor(this: Model, tooltipItem: Item): Color | void;
labelPointStyle(this: Model, tooltipItem: Item): { pointStyle: PointStyle; rotation: number } | void;
beforeFooter(this: Model, tooltipItems: Item[]): string | string[] | void;
footer(this: Model, tooltipItems: Item[]): string | string[] | void;
afterFooter(this: Model, tooltipItems: Item[]): string | string[] | void;
}
export interface ExtendedPlugin<
TType extends ChartType,
O = AnyObject,
Model = TooltipModel<TType>> {
/**
* @desc Called before drawing the `tooltip`. If any plugin returns `false`,
* the tooltip drawing is cancelled until another `render` is triggered.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {Tooltip} args.tooltip - The tooltip.
* @param {object} options - The plugin options.
* @returns {boolean} `false` to cancel the chart tooltip drawing.
*/
beforeTooltipDraw?(chart: Chart, args: { tooltip: Model, cancelable: true }, options: O): boolean | void;
/**
* @desc Called after drawing the `tooltip`. Note that this hook will not
* be called if the tooltip drawing has been previously cancelled.
* @param {Chart} chart - The chart instance.
* @param {object} args - The call arguments.
* @param {Tooltip} args.tooltip - The tooltip.
* @param {object} options - The plugin options.
*/
afterTooltipDraw?(chart: Chart, args: { tooltip: Model }, options: O): void;
}
export interface ScriptableTooltipContext<TType extends ChartType> {
chart: UnionToIntersection<Chart<TType>>;
tooltip: UnionToIntersection<TooltipModel<TType>>;
tooltipItems: TooltipItem<TType>[];
}
export interface TooltipOptions<TType extends ChartType = ChartType> extends CoreInteractionOptions {
/**
* Are on-canvas tooltips enabled?
* @default true
*/
enabled: Scriptable<boolean, ScriptableTooltipContext<TType>>;
/**
* See external tooltip section.
*/
external(this: TooltipModel<TType>, args: { chart: Chart; tooltip: TooltipModel<TType> }): void;
/**
* The mode for positioning the tooltip
*/
position: Scriptable<TooltipPositioner, ScriptableTooltipContext<TType>>
/**
* Override the tooltip alignment calculations
*/
xAlign: Scriptable<TooltipXAlignment, ScriptableTooltipContext<TType>>;
yAlign: Scriptable<TooltipYAlignment, ScriptableTooltipContext<TType>>;
/**
* Sort tooltip items.
*/
itemSort: (a: TooltipItem<TType>, b: TooltipItem<TType>, data: ChartData) => number;
filter: (e: TooltipItem<TType>, index: number, array: TooltipItem<TType>[], data: ChartData) => boolean;
/**
* Background color of the tooltip.
* @default 'rgba(0, 0, 0, 0.8)'
*/
backgroundColor: Scriptable<Color, ScriptableTooltipContext<TType>>;
/**
* Padding between the color box and the text.
* @default 1
*/
boxPadding: number;
/**
* Color of title
* @default '#fff'
*/
titleColor: Scriptable<Color, ScriptableTooltipContext<TType>>;
/**
* See Fonts
* @default {weight: 'bold'}
*/
titleFont: ScriptableAndScriptableOptions<Partial<FontSpec>, ScriptableTooltipContext<TType>>;
/**
* Spacing to add to top and bottom of each title line.
* @default 2
*/
titleSpacing: Scriptable<number, ScriptableTooltipContext<TType>>;
/**
* Margin to add on bottom of title section.
* @default 6
*/
titleMarginBottom: Scriptable<number, ScriptableTooltipContext<TType>>;
/**
* Horizontal alignment of the title text lines.
* @default 'left'
*/
titleAlign: Scriptable<TextAlign, ScriptableTooltipContext<TType>>;
/**
* Spacing to add to top and bottom of each tooltip item.
* @default 2
*/
bodySpacing: Scriptable<number, ScriptableTooltipContext<TType>>;
/**
* Color of body
* @default '#fff'
*/
bodyColor: Scriptable<Color, ScriptableTooltipContext<TType>>;
/**
* See Fonts.
* @default {}
*/
bodyFont: ScriptableAndScriptableOptions<Partial<FontSpec>, ScriptableTooltipContext<TType>>;
/**
* Horizontal alignment of the body text lines.
* @default 'left'
*/
bodyAlign: Scriptable<TextAlign, ScriptableTooltipContext<TType>>;
/**
* Spacing to add to top and bottom of each footer line.
* @default 2
*/
footerSpacing: Scriptable<number, ScriptableTooltipContext<TType>>;
/**
* Margin to add before drawing the footer.
* @default 6
*/
footerMarginTop: Scriptable<number, ScriptableTooltipContext<TType>>;
/**
* Color of footer
* @default '#fff'
*/
footerColor: Scriptable<Color, ScriptableTooltipContext<TType>>;
/**
* See Fonts
* @default {weight: 'bold'}
*/
footerFont: ScriptableAndScriptableOptions<Partial<FontSpec>, ScriptableTooltipContext<TType>>;
/**
* Horizontal alignment of the footer text lines.
* @default 'left'
*/
footerAlign: Scriptable<TextAlign, ScriptableTooltipContext<TType>>;
/**
* Padding to add to the tooltip
* @default 6
*/
padding: Scriptable<Padding, ScriptableTooltipContext<TType>>;
/**
* Extra distance to move the end of the tooltip arrow away from the tooltip point.
* @default 2
*/
caretPadding: Scriptable<number, ScriptableTooltipContext<TType>>;
/**
* Size, in px, of the tooltip arrow.
* @default 5
*/
caretSize: Scriptable<number, ScriptableTooltipContext<TType>>;
/**
* Radius of tooltip corner curves.
* @default 6
*/
cornerRadius: Scriptable<number | BorderRadius, ScriptableTooltipContext<TType>>;
/**
* Color to draw behind the colored boxes when multiple items are in the tooltip.
* @default '#fff'
*/
multiKeyBackground: Scriptable<Color, ScriptableTooltipContext<TType>>;
/**
* If true, color boxes are shown in the tooltip.
* @default true
*/
displayColors: Scriptable<boolean, ScriptableTooltipContext<TType>>;
/**
* Width of the color box if displayColors is true.
* @default bodyFont.size
*/
boxWidth: Scriptable<number, ScriptableTooltipContext<TType>>;
/**
* Height of the color box if displayColors is true.
* @default bodyFont.size
*/
boxHeight: Scriptable<number, ScriptableTooltipContext<TType>>;
/**
* Use the corresponding point style (from dataset options) instead of color boxes, ex: star, triangle etc. (size is based on the minimum value between boxWidth and boxHeight)
* @default false
*/
usePointStyle: Scriptable<boolean, ScriptableTooltipContext<TType>>;
/**
* Color of the border.
* @default 'rgba(0, 0, 0, 0)'
*/
borderColor: Scriptable<Color, ScriptableTooltipContext<TType>>;
/**
* Size of the border.
* @default 0
*/
borderWidth: Scriptable<number, ScriptableTooltipContext<TType>>;
/**
* true for rendering the legends from right to left.
*/
rtl: Scriptable<boolean, ScriptableTooltipContext<TType>>;
/**
* This will force the text direction 'rtl' or 'ltr on the canvas for rendering the tooltips, regardless of the css specified on the canvas
* @default canvas's default
*/
textDirection: Scriptable<string, ScriptableTooltipContext<TType>>;
animation: AnimationSpec<TType> | false;
animations: AnimationsSpec<TType> | false;
callbacks: TooltipCallbacks<TType>;
}
export interface TooltipDatasetOptions<TType extends ChartType = ChartType> {
callbacks: TooltipDatasetCallbacks<TType>;
}
export interface TooltipItem<TType extends ChartType> {
/**
* The chart the tooltip is being shown on
*/
chart: Chart;
/**
* Label for the tooltip
*/
label: string;
/**
* Parsed data values for the given `dataIndex` and `datasetIndex`
*/
parsed: UnionToIntersection<ParsedDataType<TType>>;
/**
* Raw data values for the given `dataIndex` and `datasetIndex`
*/
raw: unknown;
/**
* Formatted value for the tooltip
*/
formattedValue: string;
/**
* The dataset the item comes from
*/
dataset: UnionToIntersection<ChartDataset<TType>>;
/**
* Index of the dataset the item comes from
*/
datasetIndex: number;
/**
* Index of this data item in the dataset
*/
dataIndex: number;
/**
* The chart element (point, arc, bar, etc.) for this tooltip item
*/
element: Element;
}
export interface PluginDatasetOptionsByType<TType extends ChartType> {
tooltip: TooltipDatasetOptions<TType>;
}
export interface PluginOptionsByType<TType extends ChartType> {
colors: ColorsPluginOptions;
decimation: DecimationOptions;
filler: FillerOptions;
legend: LegendOptions<TType>;
subtitle: TitleOptions;
title: TitleOptions;
tooltip: TooltipOptions<TType>;
}
export interface PluginChartOptions<TType extends ChartType> {
plugins: PluginOptionsByType<TType>;
}
export interface BorderOptions {
/**
* @default true
*/
display: boolean
/**
* @default []
*/
dash: Scriptable<number[], ScriptableScaleContext>;
/**
* @default 0
*/
dashOffset: Scriptable<number, ScriptableScaleContext>;
color: Color;
width: number;
z: number;
}
export interface GridLineOptions {
/**
* @default true
*/
display: boolean;
/**
* @default false
*/
circular: boolean;
/**
* @default 'rgba(0, 0, 0, 0.1)'
*/
color: ScriptableAndArray<Color, ScriptableScaleContext>;
/**
* @default 1
*/
lineWidth: ScriptableAndArray<number, ScriptableScaleContext>;
/**
* @default true
*/
drawOnChartArea: boolean;
/**
* @default true
*/
drawTicks: boolean;
/**
* @default []
*/
tickBorderDash: Scriptable<number[], ScriptableScaleContext>;
/**
* @default 0
*/
tickBorderDashOffset: Scriptable<number, ScriptableScaleContext>;
/**
* @default 'rgba(0, 0, 0, 0.1)'
*/
tickColor: ScriptableAndArray<Color, ScriptableScaleContext>;
/**
* @default 10
*/
tickLength: number;
/**
* @default 1
*/
tickWidth: number;
/**
* @default false
*/
offset: boolean;
/**
* @default 0
*/
z: number;
}
export interface TickOptions {
/**
* Color of label backdrops.
* @default 'rgba(255, 255, 255, 0.75)'
*/
backdropColor: Scriptable<Color, ScriptableScaleContext>;
/**
* Padding of tick backdrop.
* @default 2
*/
backdropPadding: number | ChartArea;
/**
* Returns the string representation of the tick value as it should be displayed on the chart. See callback.
*/
callback: (this: Scale, tickValue: number | string, index: number, ticks: Tick[]) => string | string[] | number | number[] | null | undefined;
/**
* If true, show tick labels.
* @default true
*/
display: boolean;
/**
* Color of tick
* @see Defaults.color
*/
color: ScriptableAndArray<Color, ScriptableScaleContext>;
/**
* see Fonts
*/
font: ScriptableAndScriptableOptions<Partial<FontSpec>, ScriptableScaleContext>;
/**
* Sets the offset of the tick labels from the axis
*/
padding: number;
/**
* If true, draw a background behind the tick labels.
* @default false
*/
showLabelBackdrop: Scriptable<boolean, ScriptableScaleContext>;
/**
* The color of the stroke around the text.
* @default undefined
*/
textStrokeColor: Scriptable<Color, ScriptableScaleContext>;
/**
* Stroke width around the text.
* @default 0
*/
textStrokeWidth: Scriptable<number, ScriptableScaleContext>;
/**
* z-index of tick layer. Useful when ticks are drawn on chart area. Values <= 0 are drawn under datasets, > 0 on top.
* @default 0
*/
z: number;
major: {
/**
* If true, major ticks are generated. A major tick will affect autoskipping and major will be defined on ticks in the scriptable options context.
* @default false
*/
enabled: boolean;
};
}
export type CartesianTickOptions = TickOptions & {
/**
* The number of ticks to examine when deciding how many labels will fit. Setting a smaller value will be faster, but may be less accurate when there is large variability in label length.
* @default ticks.length
*/
sampleSize: number;
/**
* The label alignment
* @default 'center'
*/
align: Align | 'inner';
/**
* If true, automatically calculates how many labels can be shown and hides labels accordingly. Labels will be rotated up to maxRotation before skipping any. Turn autoSkip off to show all labels no matter what.
* @default true
*/
autoSkip: boolean;
/**
* Padding between the ticks on the horizontal axis when autoSkip is enabled.
* @default 0
*/
autoSkipPadding: number;
/**
* How is the label positioned perpendicular to the axis direction.
* This only applies when the rotation is 0 and the axis position is one of "top", "left", "right", or "bottom"
* @default 'near'
*/
crossAlign: 'near' | 'center' | 'far';
/**
* Should the defined `min` and `max` values be presented as ticks even if they are not "nice".
* @default: true
*/
includeBounds: boolean;
/**
* Distance in pixels to offset the label from the centre point of the tick (in the x direction for the x axis, and the y direction for the y axis). Note: this can cause labels at the edges to be cropped by the edge of the canvas
* @default 0
*/
labelOffset: number;
/**
* Minimum rotation for tick labels. Note: Only applicable to horizontal scales.
* @default 0
*/
minRotation: number;
/**
* Maximum rotation for tick labels when rotating to condense labels. Note: Rotation doesn't occur until necessary. Note: Only applicable to horizontal scales.
* @default 50
*/
maxRotation: number;
/**
* Flips tick labels around axis, displaying the labels inside the chart instead of outside. Note: Only applicable to vertical scales.
* @default false
*/
mirror: boolean;
/**
* Padding between the tick label and the axis. When set on a vertical axis, this applies in the horizontal (X) direction. When set on a horizontal axis, this applies in the vertical (Y) direction.
* @default 0
*/
padding: number;
/**
* Maximum number of ticks and gridlines to show.
* @default 11
*/
maxTicksLimit: number;
}
export interface ScriptableCartesianScaleContext {
scale: keyof CartesianScaleTypeRegistry;
type: string;
}
export interface ScriptableChartContext {
chart: Chart;
type: string;
}
export interface CartesianScaleOptions extends CoreScaleOptions {
/**
* Scale boundary strategy (bypassed by min/max time options)
* - `data`: make sure data are fully visible, ticks outside are removed
* - `ticks`: make sure ticks are fully visible, data outside are truncated
* @since 2.7.0
* @default 'ticks'
*/
bounds: 'ticks' | 'data';
/**
* Position of the axis.
*/
position: 'left' | 'top' | 'right' | 'bottom' | 'center' | { [scale: string]: number };
/**
* Stack group. Axes at the same `position` with same `stack` are stacked.
*/
stack?: string;
/**
* Weight of the scale in stack group. Used to determine the amount of allocated space for the scale within the group.
* @default 1
*/
stackWeight?: number;
/**
* Which type of axis this is. Possible values are: 'x', 'y', 'r'. If not set, this is inferred from the first character of the ID which should be 'x', 'y' or 'r'.
*/
axis: 'x' | 'y' | 'r';
/**
* User defined minimum value for the scale, overrides minimum value from data.
*/
min: number;
/**
* User defined maximum value for the scale, overrides maximum value from data.
*/
max: number;
/**
* If true, extra space is added to the both edges and the axis is scaled to fit into the chart area. This is set to true for a bar chart by default.
* @default false
*/
offset: boolean;
grid: Partial<GridLineOptions>;
border: BorderOptions;
/** Options for the scale title. */
title: {
/** If true, displays the axis title. */
display: boolean;
/** Alignment of the axis title. */
align: Align;
/** The text for the title, e.g. "# of People" or "Response Choices". */
text: string | string[];
/** Color of the axis label. */
color: Color;
/** Information about the axis title font. */
font: ScriptableAndScriptableOptions<Partial<FontSpec>, ScriptableCartesianScaleContext>;
/** Padding to apply around scale labels. */
padding: number | {
/** Padding on the (relative) top side of this axis label. */
top: number;
/** Padding on the (relative) bottom side of this axis label. */
bottom: number;
/** This is a shorthand for defining top/bottom to the same values. */
y: number;
};
};
/**
* If true, data will be comprised between datasets of data
* @default false
*/
stacked?: boolean | 'single';
ticks: CartesianTickOptions;
}
export type CategoryScaleOptions = Omit<CartesianScaleOptions, 'min' | 'max'> & {
min: string | number;
max: string | number;
labels: string[] | string[][];
};
export type CategoryScale<O extends CategoryScaleOptions = CategoryScaleOptions> = Scale<O>
export declare const CategoryScale: ChartComponent & {
prototype: CategoryScale;
new <O extends CategoryScaleOptions = CategoryScaleOptions>(cfg: AnyObject): CategoryScale<O>;
};
export type LinearScaleOptions = CartesianScaleOptions & {
/**
* if true, scale will include 0 if it is not already included.
* @default true
*/
beginAtZero: boolean;
/**
* Adjustment used when calculating the minimum data value.
*/
suggestedMin?: number;
/**
* Adjustment used when calculating the maximum data value.
*/
suggestedMax?: number;
/**
* Percentage (string ending with %) or amount (number) for added room in the scale range above and below data.
*/
grace?: string | number;
ticks: {
/**
* The Intl.NumberFormat options used by the default label formatter
*/
format: Intl.NumberFormatOptions;
/**
* if defined and stepSize is not specified, the step size will be rounded to this many decimal places.
*/
precision: number;
/**
* User defined fixed step size for the scale
*/
stepSize: number;
/**
* User defined count of ticks
*/
count: number;
};
};
export type LinearScale<O extends LinearScaleOptions = LinearScaleOptions> = Scale<O>
export declare const LinearScale: ChartComponent & {
prototype: LinearScale;
new <O extends LinearScaleOptions = LinearScaleOptions>(cfg: AnyObject): LinearScale<O>;
};
export type LogarithmicScaleOptions = CartesianScaleOptions & {
/**
* Adjustment used when calculating the maximum data value.
*/
suggestedMin?: number;
/**
* Adjustment used when calculating the minimum data value.
*/
suggestedMax?: number;
ticks: {
/**
* The Intl.NumberFormat options used by the default label formatter
*/
format: Intl.NumberFormatOptions;
};
};
export type LogarithmicScale<O extends LogarithmicScaleOptions = LogarithmicScaleOptions> = Scale<O>
export declare const LogarithmicScale: ChartComponent & {
prototype: LogarithmicScale;
new <O extends LogarithmicScaleOptions = LogarithmicScaleOptions>(cfg: AnyObject): LogarithmicScale<O>;
};
export type TimeScaleTimeOptions = {
/**
* Custom parser for dates.
*/
parser: string | ((v: unknown) => number);
/**
* If defined, dates will be rounded to the start of this unit. See Time Units below for the allowed units.
*/
round: false | TimeUnit;
/**
* If boolean and true and the unit is set to 'week', then the first day of the week will be Monday. Otherwise, it will be Sunday.
* If `number`, the index of the first day of the week (0 - Sunday, 6 - Saturday).
* @default false
*/
isoWeekday: boolean | number;
/**
* Sets how different time units are displayed.
*/
displayFormats: {
[key: string]: string;
};
/**
* The format string to use for the tooltip.
*/
tooltipFormat: string;
/**
* If defined, will force the unit to be a certain type. See Time Units section below for details.
* @default false
*/
unit: false | TimeUnit;
/**
* The minimum display format to be used for a time unit.
* @default 'millisecond'
*/
minUnit: TimeUnit;
};
export type TimeScaleTickOptions = {
/**
* Ticks generation input values:
* - 'auto': generates "optimal" ticks based on scale size and time options.
* - 'data': generates ticks from data (including labels from data `{t|x|y}` objects).
* - 'labels': generates ticks from user given `data.labels` values ONLY.
* @see https://github.com/chartjs/Chart.js/pull/4507
* @since 2.7.0
* @default 'auto'
*/
source: 'labels' | 'auto' | 'data';
/**
* The number of units between grid lines.
* @default 1
*/
stepSize: number;
};
export type TimeScaleOptions = Omit<CartesianScaleOptions, 'min' | 'max'> & {
min: string | number;
max: string | number;
suggestedMin: string | number;
suggestedMax: string | number;
/**
* Scale boundary strategy (bypassed by min/max time options)
* - `data`: make sure data are fully visible, ticks outside are removed
* - `ticks`: make sure ticks are fully visible, data outside are truncated
* @since 2.7.0
* @default 'data'
*/
bounds: 'ticks' | 'data';
/**
* If true, bar chart offsets are computed with skipped tick sizes
* @since 3.8.0
* @default false
*/
offsetAfterAutoskip: boolean;
/**
* options for creating a new adapter instance
*/
adapters: {
date: unknown;
};
time: TimeScaleTimeOptions;
ticks: TimeScaleTickOptions;
};
export interface TimeScale<O extends TimeScaleOptions = TimeScaleOptions> extends Scale<O> {
format(value: number, format?: string): string;
getDataTimestamps(): number[];
getLabelTimestamps(): string[];
normalize(values: number[]): number[];
}
export declare const TimeScale: ChartComponent & {
prototype: TimeScale;
new <O extends TimeScaleOptions = TimeScaleOptions>(cfg: AnyObject): TimeScale<O>;
};
export type TimeSeriesScale<O extends TimeScaleOptions = TimeScaleOptions> = TimeScale<O>
export declare const TimeSeriesScale: ChartComponent & {
prototype: TimeSeriesScale;
new <O extends TimeScaleOptions = TimeScaleOptions>(cfg: AnyObject): TimeSeriesScale<O>;
};
export type RadialTickOptions = TickOptions & {
/**
* The Intl.NumberFormat options used by the default label formatter
*/
format: Intl.NumberFormatOptions;
/**
* Maximum number of ticks and gridlines to show.
* @default 11
*/
maxTicksLimit: number;
/**
* if defined and stepSize is not specified, the step size will be rounded to this many decimal places.
*/
precision: number;
/**
* User defined fixed step size for the scale.
*/
stepSize: number;
/**
* User defined number of ticks
*/
count: number;
}
export type RadialLinearScaleOptions = CoreScaleOptions & {
animate: boolean;
startAngle: number;
angleLines: {
/**
* if true, angle lines are shown.
* @default true
*/
display: boolean;
/**
* Color of angled lines.
* @default 'rgba(0, 0, 0, 0.1)'
*/
color: Scriptable<Color, ScriptableScaleContext>;
/**
* Width of angled lines.
* @default 1
*/
lineWidth: Scriptable<number, ScriptableScaleContext>;
/**
* Length and spacing of dashes on angled lines. See MDN.
* @default []
*/
borderDash: Scriptable<number[], ScriptableScaleContext>;
/**
* Offset for line dashes. See MDN.
* @default 0
*/
borderDashOffset: Scriptable<number, ScriptableScaleContext>;
};
/**
* if true, scale will include 0 if it is not already included.
* @default false
*/
beginAtZero: boolean;
grid: Partial<GridLineOptions>;
/**
* User defined minimum number for the scale, overrides minimum value from data.
*/
min: number;
/**
* User defined maximum number for the scale, overrides maximum value from data.
*/
max: number;
pointLabels: {
/**
* Background color of the point label.
* @default undefined
*/
backdropColor: Scriptable<Color, ScriptableScalePointLabelContext>;
/**
* Padding of label backdrop.
* @default 2
*/
backdropPadding: Scriptable<number | ChartArea, ScriptableScalePointLabelContext>;
/**
* Border radius
* @default 0
* @since 3.8.0
*/
borderRadius: Scriptable<number | BorderRadius, ScriptableScalePointLabelContext>;
/**
* if true, point labels are shown. When `display: 'auto'`, the label is hidden if it overlaps with another label.
* @default true
*/
display: boolean | 'auto';
/**
* Color of label
* @see Defaults.color
*/
color: Scriptable<Color, ScriptableScalePointLabelContext>;
/**
*/
font: ScriptableAndScriptableOptions<Partial<FontSpec>, ScriptableScalePointLabelContext>;
/**
* Callback function to transform data labels to point labels. The default implementation simply returns the current string.
*/
callback: (label: string, index: number) => string | string[] | number | number[];
/**
* Padding around the pointLabels
* @default 5
*/
padding: Scriptable<number, ScriptableScalePointLabelContext>;
/**
* if true, point labels are centered.
* @default false
*/
centerPointLabels: boolean;
};
/**
* Adjustment used when calculating the maximum data value.
*/
suggestedMax: number;
/**
* Adjustment used when calculating the minimum data value.
*/
suggestedMin: number;
ticks: RadialTickOptions;
};
export interface RadialLinearScale<O extends RadialLinearScaleOptions = RadialLinearScaleOptions> extends Scale<O> {
xCenter: number;
yCenter: number;
readonly drawingArea: number;
setCenterPoint(leftMovement: number, rightMovement: number, topMovement: number, bottomMovement: number): void;
getIndexAngle(index: number): number;
getDistanceFromCenterForValue(value: number): number;
getValueForDistanceFromCenter(distance: number): number;
getPointPosition(index: number, distanceFromCenter: number): { x: number; y: number; angle: number };
getPointPositionForValue(index: number, value: number): { x: number; y: number; angle: number };
getPointLabelPosition(index: number): ChartArea;
getBasePosition(index: number): { x: number; y: number; angle: number };
}
export declare const RadialLinearScale: ChartComponent & {
prototype: RadialLinearScale;
new <O extends RadialLinearScaleOptions = RadialLinearScaleOptions>(cfg: AnyObject): RadialLinearScale<O>;
};
export interface CartesianScaleTypeRegistry {
linear: {
options: LinearScaleOptions;
};
logarithmic: {
options: LogarithmicScaleOptions;
};
category: {
options: CategoryScaleOptions;
};
time: {
options: TimeScaleOptions;
};
timeseries: {
options: TimeScaleOptions;
};
}
export interface RadialScaleTypeRegistry {
radialLinear: {
options: RadialLinearScaleOptions;
};
}
export interface ScaleTypeRegistry extends CartesianScaleTypeRegistry, RadialScaleTypeRegistry {
}
export type ScaleType = keyof ScaleTypeRegistry;
export interface CartesianParsedData extends Point {
// Only specified when stacked bars are enabled
_stacks?: {
// Key is the stack ID which is generally the axis ID
[key: string]: {
// Inner key is the datasetIndex
[key: number]: number;
}
}
}
export interface BarParsedData extends CartesianParsedData {
// Only specified if floating bars are show
_custom?: {
barStart: number;
barEnd: number;
start: number;
end: number;
min: number;
max: number;
}
}
export interface BubbleParsedData extends CartesianParsedData {
// The bubble radius value
_custom: number;
}
export interface RadialParsedData {
r: number;
}
export interface ChartTypeRegistry {
bar: {
chartOptions: BarControllerChartOptions;
datasetOptions: BarControllerDatasetOptions;
defaultDataPoint: number | [number, number] | null;
metaExtensions: {};
parsedDataType: BarParsedData,
scales: keyof CartesianScaleTypeRegistry;
};
line: {
chartOptions: LineControllerChartOptions;
datasetOptions: LineControllerDatasetOptions & FillerControllerDatasetOptions;
defaultDataPoint: ScatterDataPoint | number | null;
metaExtensions: {};
parsedDataType: CartesianParsedData;
scales: keyof CartesianScaleTypeRegistry;
};
scatter: {
chartOptions: ScatterControllerChartOptions;
datasetOptions: ScatterControllerDatasetOptions;
defaultDataPoint: ScatterDataPoint | number | null;
metaExtensions: {};
parsedDataType: CartesianParsedData;
scales: keyof CartesianScaleTypeRegistry;
};
bubble: {
chartOptions: unknown;
datasetOptions: BubbleControllerDatasetOptions;
defaultDataPoint: BubbleDataPoint;
metaExtensions: {};
parsedDataType: BubbleParsedData;
scales: keyof CartesianScaleTypeRegistry;
};
pie: {
chartOptions: PieControllerChartOptions;
datasetOptions: PieControllerDatasetOptions;
defaultDataPoint: PieDataPoint;
metaExtensions: PieMetaExtensions;
parsedDataType: number;
scales: keyof CartesianScaleTypeRegistry;
};
doughnut: {
chartOptions: DoughnutControllerChartOptions;
datasetOptions: DoughnutControllerDatasetOptions;
defaultDataPoint: DoughnutDataPoint;
metaExtensions: DoughnutMetaExtensions;
parsedDataType: number;
scales: keyof CartesianScaleTypeRegistry;
};
polarArea: {
chartOptions: PolarAreaControllerChartOptions;
datasetOptions: PolarAreaControllerDatasetOptions;
defaultDataPoint: number;
metaExtensions: {};
parsedDataType: RadialParsedData;
scales: keyof RadialScaleTypeRegistry;
};
radar: {
chartOptions: RadarControllerChartOptions;
datasetOptions: RadarControllerDatasetOptions & FillerControllerDatasetOptions;
defaultDataPoint: number | null;
metaExtensions: {};
parsedDataType: RadialParsedData;
scales: keyof RadialScaleTypeRegistry;
};
}
export type ChartType = keyof ChartTypeRegistry;
export type ScaleOptionsByType<TScale extends ScaleType = ScaleType> =
{ [key in ScaleType]: { type: key } & ScaleTypeRegistry[key]['options'] }[TScale]
;
// Convenience alias for creating and manipulating scale options in user code
export type ScaleOptions<TScale extends ScaleType = ScaleType> = DeepPartial<ScaleOptionsByType<TScale>>;
export type DatasetChartOptions<TType extends ChartType = ChartType> = {
[key in TType]: {
datasets: ChartTypeRegistry[key]['datasetOptions'];
};
};
export type ScaleChartOptions<TType extends ChartType = ChartType> = {
scales: {
[key: string]: ScaleOptionsByType<ChartTypeRegistry[TType]['scales']>;
};
};
export type ChartOptions<TType extends ChartType = ChartType> = Exclude<
DeepPartial<
CoreChartOptions<TType> &
ElementChartOptions<TType> &
PluginChartOptions<TType> &
DatasetChartOptions<TType> &
ScaleChartOptions<TType> &
ChartTypeRegistry[TType]['chartOptions']
>,
DeepPartial<unknown[]>
>;
export type DefaultDataPoint<TType extends ChartType> = DistributiveArray<ChartTypeRegistry[TType]['defaultDataPoint']>;
export type ParsedDataType<TType extends ChartType = ChartType> = ChartTypeRegistry[TType]['parsedDataType'];
export interface ChartDatasetProperties<TType extends ChartType, TData> {
type?: TType;
data: TData;
}
export interface ChartDatasetPropertiesCustomTypesPerDataset<TType extends ChartType, TData> {
type: TType;
data: TData;
}
export type ChartDataset<
TType extends ChartType = ChartType,
TData = DefaultDataPoint<TType>
> = DeepPartial<
{ [key in ChartType]: { type: key } & ChartTypeRegistry[key]['datasetOptions'] }[TType]
> & DeepPartial<
PluginDatasetOptionsByType<TType>
> & ChartDatasetProperties<TType, TData>;
export type ChartDatasetCustomTypesPerDataset<
TType extends ChartType = ChartType,
TData = DefaultDataPoint<TType>
> = DeepPartial<
{ [key in ChartType]: { type: key } & ChartTypeRegistry[key]['datasetOptions'] }[TType]
> & DeepPartial<
PluginDatasetOptionsByType<TType>
> & ChartDatasetPropertiesCustomTypesPerDataset<TType, TData>;
/**
* TData represents the data point type. If unspecified, a default is provided
* based on the chart type.
* TLabel represents the label type
*/
export interface ChartData<
TType extends ChartType = ChartType,
TData = DefaultDataPoint<TType>,
TLabel = unknown
> {
labels?: TLabel[];
xLabels?: TLabel[];
yLabels?: TLabel[];
datasets: ChartDataset<TType, TData>[];
}
export interface ChartDataCustomTypesPerDataset<
TType extends ChartType = ChartType,
TData = DefaultDataPoint<TType>,
TLabel = unknown
> {
labels?: TLabel[];
xLabels?: TLabel[];
yLabels?: TLabel[];
datasets: ChartDatasetCustomTypesPerDataset<TType, TData>[];
}
export interface ChartConfiguration<
TType extends ChartType = ChartType,
TData = DefaultDataPoint<TType>,
TLabel = unknown
> {
type: TType;
data: ChartData<TType, TData, TLabel>;
options?: ChartOptions<TType> | undefined;
plugins?: Plugin<TType>[];
platform?: typeof BasePlatform;
}
export interface ChartConfigurationCustomTypesPerDataset<
TType extends ChartType = ChartType,
TData = DefaultDataPoint<TType>,
TLabel = unknown
> {
data: ChartDataCustomTypesPerDataset<TType, TData, TLabel>;
options?: ChartOptions<TType> | undefined;
plugins?: Plugin<TType>[];
}
+65
View File
@@ -0,0 +1,65 @@
import {ChartArea} from './geometric.js';
export type LayoutPosition = 'left' | 'top' | 'right' | 'bottom' | 'center' | 'chartArea' | {[scaleId: string]: number};
export interface LayoutItem {
/**
* The position of the item in the chart layout. Possible values are
*/
position: LayoutPosition;
/**
* The weight used to sort the item. Higher weights are further away from the chart area
*/
weight: number;
/**
* if true, and the item is horizontal, then push vertical boxes down
*/
fullSize: boolean;
/**
* Width of item. Must be valid after update()
*/
width: number;
/**
* Height of item. Must be valid after update()
*/
height: number;
/**
* Left edge of the item. Set by layout system and cannot be used in update
*/
left: number;
/**
* Top edge of the item. Set by layout system and cannot be used in update
*/
top: number;
/**
* Right edge of the item. Set by layout system and cannot be used in update
*/
right: number;
/**
* Bottom edge of the item. Set by layout system and cannot be used in update
*/
bottom: number;
/**
* Called before the layout process starts
*/
beforeLayout?(): void;
/**
* Draws the element
*/
draw(chartArea: ChartArea): void;
/**
* Returns an object with padding on the edges
*/
getPadding?(): ChartArea;
/**
* returns true if the layout item is horizontal (ie. top or bottom)
*/
isHorizontal(): boolean;
/**
* Takes two parameters: width and height.
* @param width
* @param height
*/
update(width: number, height: number, margins?: ChartArea): void;
}
+30
View File
@@ -0,0 +1,30 @@
/* eslint-disable @typescript-eslint/ban-types */
// DeepPartial implementation taken from the utility-types NPM package, which is
// Copyright (c) 2016 Piotr Witek <piotrek.witek@gmail.com> (http://piotrwitek.github.io)
// and used under the terms of the MIT license
export type DeepPartial<T> = T extends Function
? T
: T extends Array<infer U>
? _DeepPartialArray<U>
: T extends object
? _DeepPartialObject<T>
: T | undefined;
type _DeepPartialArray<T> = Array<DeepPartial<T>>
type _DeepPartialObject<T> = { [P in keyof T]?: DeepPartial<T[P]> };
export type DistributiveArray<T> = [T] extends [unknown] ? Array<T> : never
// https://stackoverflow.com/a/50375286
export type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
export type AllKeys<T> = T extends any ? keyof T : never;
export type PickType<T, K extends AllKeys<T>> = T extends { [k in K]?: any }
? T[K]
: undefined;
export type Merge<T extends object> = {
[k in AllKeys<T>]: PickType<T, k>;
};
+1
View File
@@ -0,0 +1 @@
module.exports = require('../dist/helpers.cjs');
+1
View File
@@ -0,0 +1 @@
export * from '../dist/helpers/index.js';
+1
View File
@@ -0,0 +1 @@
export * from '../dist/helpers.js';
+14
View File
@@ -0,0 +1,14 @@
{
"name": "chart.js-helpers",
"private": true,
"description": "Helpers package. Exists to support bundlers without exports support such as webpack 4.",
"type": "module",
"main": "./helpers.cjs",
"module": "./helpers.js",
"exports": {
"types": "./helpers.d.ts",
"import": "./helpers.js",
"require": "./helpers.cjs"
},
"types": "./helpers.d.ts"
}
+139
View File
@@ -0,0 +1,139 @@
{
"name": "chart.js",
"homepage": "https://www.chartjs.org",
"description": "Simple HTML5 charts using the canvas element.",
"version": "4.5.1",
"license": "MIT",
"type": "module",
"sideEffects": [
"./auto/auto.js",
"./auto/auto.cjs",
"./dist/chart.umd.min.js",
"./dist/chart.umd.js"
],
"jsdelivr": "./dist/chart.umd.min.js",
"unpkg": "./dist/chart.umd.min.js",
"main": "./dist/chart.cjs",
"module": "./dist/chart.js",
"exports": {
".": {
"types": "./dist/types.d.ts",
"import": "./dist/chart.js",
"require": "./dist/chart.cjs"
},
"./auto": {
"types": "./auto/auto.d.ts",
"import": "./auto/auto.js",
"require": "./auto/auto.cjs"
},
"./helpers": {
"types": "./helpers/helpers.d.ts",
"import": "./helpers/helpers.js",
"require": "./helpers/helpers.cjs"
}
},
"types": "./dist/types.d.ts",
"keywords": [
"canvas",
"charts",
"data",
"graphs",
"html5",
"responsive"
],
"repository": {
"type": "git",
"url": "https://github.com/chartjs/Chart.js.git"
},
"bugs": {
"url": "https://github.com/chartjs/Chart.js/issues"
},
"files": [
"auto/**",
"dist/**",
"!dist/docs/**",
"helpers/**"
],
"scripts": {
"autobuild": "rollup -c -w",
"copyDeclarations": "node -e \"fs.cpSync('./src/types/', './dist/types/', {recursive:true})\"",
"emitDeclarations": "tsc --emitDeclarationOnly && pnpm copyDeclarations",
"build": "rollup -c && pnpm emitDeclarations",
"dev": "karma start ./karma.conf.cjs --auto-watch --no-single-run --browsers chrome --grep",
"dev:ff": "karma start ./karma.conf.cjs --auto-watch --no-single-run --browsers firefox --grep",
"docs": "pnpm run build && pnpm --filter \"./docs/**\" build",
"docs:dev": "pnpm run build && pnpm --filter \"./docs/**\" dev",
"lint-js": "eslint \"src/**/*.{js,ts}\" \"test/**/*.js\" \"docs/**/*.js\" --cache",
"lint-md": "eslint \"**/*.md\" --cache",
"lint-types": "pnpm build && node test/types/autogen.js && tsc -p test/types",
"lint": "concurrently \"pnpm:lint-*\"",
"test": "pnpm lint && pnpm test-ci",
"test-ci": "concurrently \"pnpm:test-ci-*\"",
"test-ci-karma": "cross-env NODE_ENV=test karma start ./karma.conf.cjs --auto-watch --single-run --coverage --grep",
"test-ci-integration": "pnpm --filter \"./test/integration/**\" test"
},
"dependencies": {
"@kurkle/color": "^0.3.0"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^23.0.2",
"@rollup/plugin-inject": "^5.0.2",
"@rollup/plugin-json": "^5.0.1",
"@rollup/plugin-node-resolve": "^15.0.1",
"@swc/core": "^1.3.18",
"@types/estree": "^1.0.0",
"@types/offscreencanvas": "^2019.7.0",
"@typescript-eslint/eslint-plugin": "^5.32.0",
"@typescript-eslint/parser": "^5.32.0",
"chartjs-adapter-luxon": "^1.2.0",
"chartjs-adapter-moment": "^1.0.0",
"chartjs-test-utils": "^0.4.0",
"concurrently": "^7.3.0",
"coveralls": "^3.1.1",
"cross-env": "^7.0.3",
"eslint": "^8.21.0",
"eslint-config-chartjs": "^0.3.0",
"eslint-plugin-es": "^4.1.0",
"eslint-plugin-html": "^7.1.0",
"eslint-plugin-markdown": "^3.0.0",
"esm": "^3.2.25",
"glob": "^8.0.3",
"jasmine": "^3.7.0",
"jasmine-core": "^3.7.1",
"karma": "^6.3.2",
"karma-chrome-launcher": "^3.1.0",
"karma-coverage": "^2.0.3",
"karma-edge-launcher": "^0.4.2",
"karma-firefox-launcher": "^2.1.0",
"karma-jasmine": "^4.0.1",
"karma-jasmine-html-reporter": "^1.5.4",
"karma-rollup-preprocessor": "7.0.7",
"karma-safari-private-launcher": "^1.0.0",
"karma-spec-reporter": "0.0.32",
"luxon": "^3.0.1",
"moment": "^2.29.4",
"moment-timezone": "^0.5.34",
"pixelmatch": "^5.3.0",
"rollup": "^3.3.0",
"rollup-plugin-cleanup": "^3.2.1",
"rollup-plugin-istanbul": "^4.0.0",
"rollup-plugin-swc3": "^0.7.0",
"rollup-plugin-terser": "^7.0.2",
"typescript": "^4.7.4",
"yargs": "^17.5.1"
},
"engines": {
"pnpm": ">=8"
},
"packageManager": "pnpm@8.13.0",
"pnpm": {
"overrides": {
"html-entities": "1.4.0"
},
"peerDependencyRules": {
"ignoreMissing": [
"chart.js"
]
}
}
}