terminal-revenue-trends-chart
Version:
Bloomberg-style terminal revenue trends chart component
1,774 lines (1,747 loc) • 416 kB
JavaScript
import { jsxs, jsx } from 'react/jsx-runtime';
import React, { forwardRef, useRef, useEffect } from 'react';
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
function __spreadArray(to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
}
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
/*!
* @kurkle/color v0.3.4
* https://github.com/kurkle/color#readme
* (c) 2024 Jukka Kurkela
* Released under the MIT License
*/
function round(v) {
return v + 0.5 | 0;
}
const lim = (v, l, h) => Math.max(Math.min(v, h), l);
function p2b(v) {
return lim(round(v * 2.55), 0, 255);
}
function n2b(v) {
return lim(round(v * 255), 0, 255);
}
function b2n(v) {
return lim(round(v / 2.55) / 100, 0, 1);
}
function n2p(v) {
return lim(round(v * 100), 0, 100);
}
const map$1 = {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, A: 10, B: 11, C: 12, D: 13, E: 14, F: 15, a: 10, b: 11, c: 12, d: 13, e: 14, f: 15};
const hex = [...'0123456789ABCDEF'];
const h1 = b => hex[b & 0xF];
const h2 = b => hex[(b & 0xF0) >> 4] + hex[b & 0xF];
const eq = b => ((b & 0xF0) >> 4) === (b & 0xF);
const isShort = v => eq(v.r) && eq(v.g) && eq(v.b) && eq(v.a);
function hexParse(str) {
var len = str.length;
var ret;
if (str[0] === '#') {
if (len === 4 || len === 5) {
ret = {
r: 255 & map$1[str[1]] * 17,
g: 255 & map$1[str[2]] * 17,
b: 255 & map$1[str[3]] * 17,
a: len === 5 ? map$1[str[4]] * 17 : 255
};
} else if (len === 7 || len === 9) {
ret = {
r: map$1[str[1]] << 4 | map$1[str[2]],
g: map$1[str[3]] << 4 | map$1[str[4]],
b: map$1[str[5]] << 4 | map$1[str[6]],
a: len === 9 ? (map$1[str[7]] << 4 | map$1[str[8]]) : 255
};
}
}
return ret;
}
const alpha = (a, f) => a < 255 ? f(a) : '';
function hexString(v) {
var f = isShort(v) ? h1 : h2;
return v
? '#' + f(v.r) + f(v.g) + f(v.b) + alpha(v.a, f)
: undefined;
}
const HUE_RE = /^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;
function hsl2rgbn(h, s, l) {
const a = s * Math.min(l, 1 - l);
const f = (n, k = (n + h / 30) % 12) => l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
return [f(0), f(8), f(4)];
}
function hsv2rgbn(h, s, v) {
const f = (n, k = (n + h / 60) % 6) => v - v * s * Math.max(Math.min(k, 4 - k, 1), 0);
return [f(5), f(3), f(1)];
}
function hwb2rgbn(h, w, b) {
const rgb = hsl2rgbn(h, 1, 0.5);
let i;
if (w + b > 1) {
i = 1 / (w + b);
w *= i;
b *= i;
}
for (i = 0; i < 3; i++) {
rgb[i] *= 1 - w - b;
rgb[i] += w;
}
return rgb;
}
function hueValue(r, g, b, d, max) {
if (r === max) {
return ((g - b) / d) + (g < b ? 6 : 0);
}
if (g === max) {
return (b - r) / d + 2;
}
return (r - g) / d + 4;
}
function rgb2hsl(v) {
const range = 255;
const r = v.r / range;
const g = v.g / range;
const b = v.b / range;
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
const l = (max + min) / 2;
let h, s, d;
if (max !== min) {
d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
h = hueValue(r, g, b, d, max);
h = h * 60 + 0.5;
}
return [h | 0, s || 0, l];
}
function calln(f, a, b, c) {
return (
Array.isArray(a)
? f(a[0], a[1], a[2])
: f(a, b, c)
).map(n2b);
}
function hsl2rgb(h, s, l) {
return calln(hsl2rgbn, h, s, l);
}
function hwb2rgb(h, w, b) {
return calln(hwb2rgbn, h, w, b);
}
function hsv2rgb(h, s, v) {
return calln(hsv2rgbn, h, s, v);
}
function hue(h) {
return (h % 360 + 360) % 360;
}
function hueParse(str) {
const m = HUE_RE.exec(str);
let a = 255;
let v;
if (!m) {
return;
}
if (m[5] !== v) {
a = m[6] ? p2b(+m[5]) : n2b(+m[5]);
}
const h = hue(+m[2]);
const p1 = +m[3] / 100;
const p2 = +m[4] / 100;
if (m[1] === 'hwb') {
v = hwb2rgb(h, p1, p2);
} else if (m[1] === 'hsv') {
v = hsv2rgb(h, p1, p2);
} else {
v = hsl2rgb(h, p1, p2);
}
return {
r: v[0],
g: v[1],
b: v[2],
a: a
};
}
function rotate(v, deg) {
var h = rgb2hsl(v);
h[0] = hue(h[0] + deg);
h = hsl2rgb(h);
v.r = h[0];
v.g = h[1];
v.b = h[2];
}
function hslString(v) {
if (!v) {
return;
}
const a = rgb2hsl(v);
const h = a[0];
const s = n2p(a[1]);
const l = n2p(a[2]);
return v.a < 255
? `hsla(${h}, ${s}%, ${l}%, ${b2n(v.a)})`
: `hsl(${h}, ${s}%, ${l}%)`;
}
const map = {
x: 'dark',
Z: 'light',
Y: 're',
X: 'blu',
W: 'gr',
V: 'medium',
U: 'slate',
A: 'ee',
T: 'ol',
S: 'or',
B: 'ra',
C: 'lateg',
D: 'ights',
R: 'in',
Q: 'turquois',
E: 'hi',
P: 'ro',
O: 'al',
N: 'le',
M: 'de',
L: 'yello',
F: 'en',
K: 'ch',
G: 'arks',
H: 'ea',
I: 'ightg',
J: 'wh'
};
const names$1 = {
OiceXe: 'f0f8ff',
antiquewEte: 'faebd7',
aqua: 'ffff',
aquamarRe: '7fffd4',
azuY: 'f0ffff',
beige: 'f5f5dc',
bisque: 'ffe4c4',
black: '0',
blanKedOmond: 'ffebcd',
Xe: 'ff',
XeviTet: '8a2be2',
bPwn: 'a52a2a',
burlywood: 'deb887',
caMtXe: '5f9ea0',
KartYuse: '7fff00',
KocTate: 'd2691e',
cSO: 'ff7f50',
cSnflowerXe: '6495ed',
cSnsilk: 'fff8dc',
crimson: 'dc143c',
cyan: 'ffff',
xXe: '8b',
xcyan: '8b8b',
xgTMnPd: 'b8860b',
xWay: 'a9a9a9',
xgYF: '6400',
xgYy: 'a9a9a9',
xkhaki: 'bdb76b',
xmagFta: '8b008b',
xTivegYF: '556b2f',
xSange: 'ff8c00',
xScEd: '9932cc',
xYd: '8b0000',
xsOmon: 'e9967a',
xsHgYF: '8fbc8f',
xUXe: '483d8b',
xUWay: '2f4f4f',
xUgYy: '2f4f4f',
xQe: 'ced1',
xviTet: '9400d3',
dAppRk: 'ff1493',
dApskyXe: 'bfff',
dimWay: '696969',
dimgYy: '696969',
dodgerXe: '1e90ff',
fiYbrick: 'b22222',
flSOwEte: 'fffaf0',
foYstWAn: '228b22',
fuKsia: 'ff00ff',
gaRsbSo: 'dcdcdc',
ghostwEte: 'f8f8ff',
gTd: 'ffd700',
gTMnPd: 'daa520',
Way: '808080',
gYF: '8000',
gYFLw: 'adff2f',
gYy: '808080',
honeyMw: 'f0fff0',
hotpRk: 'ff69b4',
RdianYd: 'cd5c5c',
Rdigo: '4b0082',
ivSy: 'fffff0',
khaki: 'f0e68c',
lavFMr: 'e6e6fa',
lavFMrXsh: 'fff0f5',
lawngYF: '7cfc00',
NmoncEffon: 'fffacd',
ZXe: 'add8e6',
ZcSO: 'f08080',
Zcyan: 'e0ffff',
ZgTMnPdLw: 'fafad2',
ZWay: 'd3d3d3',
ZgYF: '90ee90',
ZgYy: 'd3d3d3',
ZpRk: 'ffb6c1',
ZsOmon: 'ffa07a',
ZsHgYF: '20b2aa',
ZskyXe: '87cefa',
ZUWay: '778899',
ZUgYy: '778899',
ZstAlXe: 'b0c4de',
ZLw: 'ffffe0',
lime: 'ff00',
limegYF: '32cd32',
lRF: 'faf0e6',
magFta: 'ff00ff',
maPon: '800000',
VaquamarRe: '66cdaa',
VXe: 'cd',
VScEd: 'ba55d3',
VpurpN: '9370db',
VsHgYF: '3cb371',
VUXe: '7b68ee',
VsprRggYF: 'fa9a',
VQe: '48d1cc',
VviTetYd: 'c71585',
midnightXe: '191970',
mRtcYam: 'f5fffa',
mistyPse: 'ffe4e1',
moccasR: 'ffe4b5',
navajowEte: 'ffdead',
navy: '80',
Tdlace: 'fdf5e6',
Tive: '808000',
TivedBb: '6b8e23',
Sange: 'ffa500',
SangeYd: 'ff4500',
ScEd: 'da70d6',
pOegTMnPd: 'eee8aa',
pOegYF: '98fb98',
pOeQe: 'afeeee',
pOeviTetYd: 'db7093',
papayawEp: 'ffefd5',
pHKpuff: 'ffdab9',
peru: 'cd853f',
pRk: 'ffc0cb',
plum: 'dda0dd',
powMrXe: 'b0e0e6',
purpN: '800080',
YbeccapurpN: '663399',
Yd: 'ff0000',
Psybrown: 'bc8f8f',
PyOXe: '4169e1',
saddNbPwn: '8b4513',
sOmon: 'fa8072',
sandybPwn: 'f4a460',
sHgYF: '2e8b57',
sHshell: 'fff5ee',
siFna: 'a0522d',
silver: 'c0c0c0',
skyXe: '87ceeb',
UXe: '6a5acd',
UWay: '708090',
UgYy: '708090',
snow: 'fffafa',
sprRggYF: 'ff7f',
stAlXe: '4682b4',
tan: 'd2b48c',
teO: '8080',
tEstN: 'd8bfd8',
tomato: 'ff6347',
Qe: '40e0d0',
viTet: 'ee82ee',
JHt: 'f5deb3',
wEte: 'ffffff',
wEtesmoke: 'f5f5f5',
Lw: 'ffff00',
LwgYF: '9acd32'
};
function unpack() {
const unpacked = {};
const keys = Object.keys(names$1);
const tkeys = Object.keys(map);
let i, j, k, ok, nk;
for (i = 0; i < keys.length; i++) {
ok = nk = keys[i];
for (j = 0; j < tkeys.length; j++) {
k = tkeys[j];
nk = nk.replace(k, map[k]);
}
k = parseInt(names$1[ok], 16);
unpacked[nk] = [k >> 16 & 0xFF, k >> 8 & 0xFF, k & 0xFF];
}
return unpacked;
}
let names;
function nameParse(str) {
if (!names) {
names = unpack();
names.transparent = [0, 0, 0, 0];
}
const a = names[str.toLowerCase()];
return a && {
r: a[0],
g: a[1],
b: a[2],
a: a.length === 4 ? a[3] : 255
};
}
const RGB_RE = /^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/;
function rgbParse(str) {
const m = RGB_RE.exec(str);
let a = 255;
let r, g, b;
if (!m) {
return;
}
if (m[7] !== r) {
const v = +m[7];
a = m[8] ? p2b(v) : lim(v * 255, 0, 255);
}
r = +m[1];
g = +m[3];
b = +m[5];
r = 255 & (m[2] ? p2b(r) : lim(r, 0, 255));
g = 255 & (m[4] ? p2b(g) : lim(g, 0, 255));
b = 255 & (m[6] ? p2b(b) : lim(b, 0, 255));
return {
r: r,
g: g,
b: b,
a: a
};
}
function rgbString(v) {
return v && (
v.a < 255
? `rgba(${v.r}, ${v.g}, ${v.b}, ${b2n(v.a)})`
: `rgb(${v.r}, ${v.g}, ${v.b})`
);
}
const to = v => v <= 0.0031308 ? v * 12.92 : Math.pow(v, 1.0 / 2.4) * 1.055 - 0.055;
const from = v => v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
function interpolate$1(rgb1, rgb2, t) {
const r = from(b2n(rgb1.r));
const g = from(b2n(rgb1.g));
const b = from(b2n(rgb1.b));
return {
r: n2b(to(r + t * (from(b2n(rgb2.r)) - r))),
g: n2b(to(g + t * (from(b2n(rgb2.g)) - g))),
b: n2b(to(b + t * (from(b2n(rgb2.b)) - b))),
a: rgb1.a + t * (rgb2.a - rgb1.a)
};
}
function modHSL(v, i, ratio) {
if (v) {
let tmp = rgb2hsl(v);
tmp[i] = Math.max(0, Math.min(tmp[i] + tmp[i] * ratio, i === 0 ? 360 : 1));
tmp = hsl2rgb(tmp);
v.r = tmp[0];
v.g = tmp[1];
v.b = tmp[2];
}
}
function clone$1(v, proto) {
return v ? Object.assign(proto || {}, v) : v;
}
function fromObject(input) {
var v = {r: 0, g: 0, b: 0, a: 255};
if (Array.isArray(input)) {
if (input.length >= 3) {
v = {r: input[0], g: input[1], b: input[2], a: 255};
if (input.length > 3) {
v.a = n2b(input[3]);
}
}
} else {
v = clone$1(input, {r: 0, g: 0, b: 0, a: 1});
v.a = n2b(v.a);
}
return v;
}
function functionParse(str) {
if (str.charAt(0) === 'r') {
return rgbParse(str);
}
return hueParse(str);
}
class Color {
constructor(input) {
if (input instanceof Color) {
return input;
}
const type = typeof input;
let v;
if (type === 'object') {
v = fromObject(input);
} else if (type === 'string') {
v = hexParse(input) || nameParse(input) || functionParse(input);
}
this._rgb = v;
this._valid = !!v;
}
get valid() {
return this._valid;
}
get rgb() {
var v = clone$1(this._rgb);
if (v) {
v.a = b2n(v.a);
}
return v;
}
set rgb(obj) {
this._rgb = fromObject(obj);
}
rgbString() {
return this._valid ? rgbString(this._rgb) : undefined;
}
hexString() {
return this._valid ? hexString(this._rgb) : undefined;
}
hslString() {
return this._valid ? hslString(this._rgb) : undefined;
}
mix(color, weight) {
if (color) {
const c1 = this.rgb;
const c2 = color.rgb;
let w2;
const p = weight === w2 ? 0.5 : weight;
const w = 2 * p - 1;
const a = c1.a - c2.a;
const w1 = ((w * a === -1 ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
w2 = 1 - w1;
c1.r = 0xFF & w1 * c1.r + w2 * c2.r + 0.5;
c1.g = 0xFF & w1 * c1.g + w2 * c2.g + 0.5;
c1.b = 0xFF & w1 * c1.b + w2 * c2.b + 0.5;
c1.a = p * c1.a + (1 - p) * c2.a;
this.rgb = c1;
}
return this;
}
interpolate(color, t) {
if (color) {
this._rgb = interpolate$1(this._rgb, color._rgb, t);
}
return this;
}
clone() {
return new Color(this.rgb);
}
alpha(a) {
this._rgb.a = n2b(a);
return this;
}
clearer(ratio) {
const rgb = this._rgb;
rgb.a *= 1 - ratio;
return this;
}
greyscale() {
const rgb = this._rgb;
const val = round(rgb.r * 0.3 + rgb.g * 0.59 + rgb.b * 0.11);
rgb.r = rgb.g = rgb.b = val;
return this;
}
opaquer(ratio) {
const rgb = this._rgb;
rgb.a *= 1 + ratio;
return this;
}
negate() {
const v = this._rgb;
v.r = 255 - v.r;
v.g = 255 - v.g;
v.b = 255 - v.b;
return this;
}
lighten(ratio) {
modHSL(this._rgb, 2, ratio);
return this;
}
darken(ratio) {
modHSL(this._rgb, 2, -ratio);
return this;
}
saturate(ratio) {
modHSL(this._rgb, 1, ratio);
return this;
}
desaturate(ratio) {
modHSL(this._rgb, 1, -ratio);
return this;
}
rotate(deg) {
rotate(this._rgb, deg);
return this;
}
}
/*!
* Chart.js v4.5.0
* https://www.chartjs.org
* (c) 2025 Chart.js Contributors
* Released under the MIT License
*/
/**
* @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 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;
{
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);
}
}
// 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);
}
/**
* 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);
}};
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;
}
/**
* 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();
retur