apexcharts
Version:
A JavaScript Chart Library
34,070 lines • 1.1 MB
JavaScript
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
var __async = (__this, __arguments, generator) => {
return new Promise((resolve, reject) => {
var fulfilled = (value) => {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
};
var rejected = (value) => {
try {
step(generator.throw(value));
} catch (e) {
reject(e);
}
};
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
step((generator = generator.apply(__this, __arguments)).next());
});
};
/*!
* ApexCharts v5.11.0
* (c) 2018-2026 ApexCharts
*/
class Environment {
/**
* Check if running in server-side rendering environment (Node.js)
* @returns {boolean} True if in SSR/Node.js, false if in browser
*/
static isSSR() {
return typeof window === "undefined" || typeof document === "undefined";
}
/**
* Check if running in browser environment
* @returns {boolean} True if in browser, false if in SSR/Node.js
*/
static isBrowser() {
return !this.isSSR();
}
/**
* Check if a specific browser API is available
* @param {string} api - Name of the API to check (e.g., 'ResizeObserver')
* @returns {boolean} True if API is available
*/
static hasAPI(api) {
if (this.isSSR()) return false;
return typeof /** @type {any} */
window[api] !== "undefined";
}
/**
* Returns the global Apex config object regardless of environment.
* In browser: window.Apex; in SSR/Node.js: global.Apex; fallback: {}.
* @returns {any}
*/
static getApex() {
if (typeof window !== "undefined" && window.Apex) return window.Apex;
if (typeof global !== "undefined" && global.Apex) return global.Apex;
return {};
}
}
class SSRElement {
/**
* @param {string} nodeName
* @param {any} namespaceURI
*/
constructor(nodeName, namespaceURI = null) {
this.nodeName = nodeName;
this.namespaceURI = namespaceURI;
this.attributes = /* @__PURE__ */ new Map();
this.children = [];
this.textContent = "";
this.style = {};
this.classList = new SSRClassList();
this.parentNode = /** @type {SSRElement | null} */
null;
this._ssrWidth = void 0;
this._ssrHeight = void 0;
this._ssrMode = void 0;
}
/**
* @param {string} name
* @param {any} value
*/
setAttribute(name2, value) {
this.attributes.set(name2, value);
}
/**
* @param {string} name
*/
getAttribute(name2) {
return this.attributes.get(name2);
}
/**
* @param {string} name
*/
removeAttribute(name2) {
this.attributes.delete(name2);
}
/**
* @param {string} name
*/
hasAttribute(name2) {
return this.attributes.has(name2);
}
/**
* @param {any} child
*/
appendChild(child) {
if (child && child !== this) {
if (child.parentNode && child.parentNode !== this) {
child.parentNode.removeChild(child);
} else if (child.parentNode === this) {
const index = this.children.indexOf(child);
if (index !== -1) this.children.splice(index, 1);
}
child.parentNode = this;
this.children.push(child);
}
return child;
}
/**
* @param {any} child
*/
removeChild(child) {
const index = this.children.indexOf(child);
if (index !== -1) {
this.children.splice(index, 1);
child.parentNode = null;
}
return child;
}
/**
* @param {any} newNode
* @param {any} referenceNode
*/
insertBefore(newNode, referenceNode) {
if (!referenceNode) {
return this.appendChild(newNode);
}
if (newNode.parentNode && newNode.parentNode !== this) {
newNode.parentNode.removeChild(newNode);
} else if (newNode.parentNode === this) {
const existingIndex = this.children.indexOf(newNode);
if (existingIndex !== -1) this.children.splice(existingIndex, 1);
}
const index = this.children.indexOf(referenceNode);
if (index !== -1) {
newNode.parentNode = this;
this.children.splice(index, 0, newNode);
}
return newNode;
}
cloneNode(deep = false) {
const clone = new SSRElement(this.nodeName, this.namespaceURI);
clone.textContent = this.textContent;
this.attributes.forEach((value, key) => {
clone.attributes.set(key, value);
});
Object.assign(clone.style, this.style);
if (deep) {
this.children.forEach((child) => {
if (child.cloneNode) {
clone.appendChild(child.cloneNode(true));
}
});
}
return clone;
}
getBoundingClientRect() {
return {
width: this._ssrWidth || 0,
height: this._ssrHeight || 0,
top: 0,
left: 0,
right: this._ssrWidth || 0,
bottom: this._ssrHeight || 0,
x: 0,
y: 0
};
}
getRootNode() {
let root = this;
while (root.parentNode) {
root = root.parentNode;
}
return root;
}
querySelector() {
return null;
}
querySelectorAll() {
return [];
}
getElementsByClassName() {
return [];
}
addEventListener() {
}
removeEventListener() {
}
get childNodes() {
return this.children;
}
toString() {
let attrs = "";
this.attributes.forEach((value, key) => {
attrs += ` ${key}="${value}"`;
});
if (this.children.length === 0 && !this.textContent) {
return `<${this.nodeName}${attrs}/>`;
}
const childrenStr = this.children.map((c) => c.toString()).join("");
return `<${this.nodeName}${attrs}>${this.textContent}${childrenStr}</${this.nodeName}>`;
}
// Property getters/setters
get innerHTML() {
return this.children.map((c) => c.toString()).join("");
}
set innerHTML(value) {
this.children = [];
this.textContent = value;
}
get outerHTML() {
return this.toString();
}
get isConnected() {
return true;
}
}
class SSRClassList {
constructor() {
this.classes = /* @__PURE__ */ new Set();
}
add(...classNames) {
classNames.forEach((name2) => this.classes.add(name2));
}
remove(...classNames) {
classNames.forEach((name2) => this.classes.delete(name2));
}
/**
* @param {string} className
*/
contains(className) {
return this.classes.has(className);
}
/**
* @param {string} className
* @param {any} force
*/
toggle(className, force) {
if (force === true) {
this.classes.add(className);
return true;
} else if (force === false) {
this.classes.delete(className);
return false;
} else {
if (this.classes.has(className)) {
this.classes.delete(className);
return false;
} else {
this.classes.add(className);
return true;
}
}
}
toString() {
return Array.from(this.classes).join(" ");
}
}
class SSRDOMShim {
constructor() {
this.SVGNS = "http://www.w3.org/2000/svg";
this.XLINKNS = "http://www.w3.org/1999/xlink";
}
/**
* Create SVG element with namespace
* @param {string} namespaceURI - Namespace URI
* @param {string} qualifiedName - Element tag name
* @returns {SSRElement} Mock SVG element
*/
createElementNS(namespaceURI, qualifiedName) {
return new SSRElement(qualifiedName, namespaceURI);
}
/**
* Create text node
* @param {string} data - Text content
* @returns {object} Text node mock
*/
createTextNode(data) {
const node = {
nodeName: "#text",
nodeType: 3,
textContent: data,
toString() {
return node.textContent;
}
};
return node;
}
/**
* Query selector (returns null in SSR)
* @returns {null}
*/
querySelector() {
return null;
}
/**
* Query selector all (returns empty array in SSR)
* @returns {any[]}
*/
querySelectorAll() {
return [];
}
/**
* Get computed style (returns empty object in SSR)
* @returns {object}
*/
getComputedStyle() {
return {};
}
/**
* Get bounding client rect for element
* @param {SSRElement} element - Element to measure
* @returns {object} Mock dimensions
*/
getBoundingClientRect(element) {
if (element && element.getBoundingClientRect) {
return element.getBoundingClientRect();
}
return {
width: 0,
height: 0,
top: 0,
left: 0,
right: 0,
bottom: 0,
x: 0,
y: 0
};
}
/**
* Create mock XMLSerializer for SSR
* @returns {object} XMLSerializer mock
*/
createXMLSerializer() {
return {
/**
* @param {Element} element
*/
serializeToString(element) {
return element.toString ? element.toString() : "";
}
};
}
/**
* Create mock DOMParser for SSR
* @returns {object} DOMParser mock
*/
createDOMParser() {
return {
/**
* @param {string} str
* @param {string} _type
*/
parseFromString(str, _type) {
const root = new SSRElement("root");
root.innerHTML = str;
return {
documentElement: root
};
}
};
}
}
let shim = null;
let xmlSerializerInstance = null;
let domParserInstance = null;
class BrowserAPIs {
/**
* Initialize the SSR shim if in SSR environment
* Must be called before using other methods
*/
static init() {
if (Environment.isSSR() && !shim) {
shim = new SSRDOMShim();
}
}
/**
* Create an HTML element
* @param {string} tagName - Element tag name
* @returns {HTMLElement} HTML element
*/
static createElement(tagName) {
if (Environment.isSSR()) {
if (!shim) this.init();
return shim.createElementNS(null, tagName);
}
return document.createElement(tagName);
}
/**
* Create an SVG element with namespace
* @param {string} namespaceURI - Namespace URI
* @param {string} qualifiedName - Element tag name
* @returns {HTMLElement} created element
*/
static createElementNS(namespaceURI, qualifiedName) {
if (Environment.isSSR()) {
if (!shim) this.init();
return shim.createElementNS(namespaceURI, qualifiedName);
}
return (
/** @type {HTMLElement} */
document.createElementNS(namespaceURI, qualifiedName)
);
}
/**
* Create a text node
* @param {string} data - Text content
* @returns {Text|object} Text node
*/
static createTextNode(data) {
if (Environment.isSSR()) {
if (!shim) this.init();
return shim.createTextNode(data);
}
return document.createTextNode(data);
}
/**
* Query selector
* @param {string} selector - CSS selector
* @returns {Element|null}
*/
static querySelector(selector) {
if (Environment.isSSR()) {
return null;
}
return document.querySelector(selector);
}
/**
* Query selector all
* @param {string} selector - CSS selector
* @returns {NodeList|any[]}
*/
static querySelectorAll(selector) {
if (Environment.isSSR()) {
return [];
}
return document.querySelectorAll(selector);
}
/**
* Get computed style for an element
* @param {Element} element - Element to get styles for
* @returns {CSSStyleDeclaration|object}
*/
static getComputedStyle(element) {
if (Environment.isSSR()) {
return {};
}
return window.getComputedStyle(element);
}
/**
* Get bounding client rect for an element
* @param {Element} element - Element to measure
* @returns {DOMRect|object}
*/
static getBoundingClientRect(element) {
if (Environment.isSSR()) {
if (!shim) this.init();
return shim.getBoundingClientRect(element);
}
return element ? element.getBoundingClientRect() : {
width: 0,
height: 0,
top: 0,
left: 0,
right: 0,
bottom: 0,
x: 0,
y: 0
};
}
/**
* Get XMLSerializer instance
* @returns {XMLSerializer|object}
*/
static getXMLSerializer() {
if (Environment.isSSR()) {
if (!shim) this.init();
if (!xmlSerializerInstance) {
xmlSerializerInstance = shim.createXMLSerializer();
}
return xmlSerializerInstance;
}
if (!xmlSerializerInstance) {
xmlSerializerInstance = new XMLSerializer();
}
return xmlSerializerInstance;
}
/**
* Get DOMParser instance
* @returns {DOMParser|object}
*/
static getDOMParser() {
if (Environment.isSSR()) {
if (!shim) this.init();
if (!domParserInstance) {
domParserInstance = shim.createDOMParser();
}
return domParserInstance;
}
if (!domParserInstance) {
domParserInstance = new DOMParser();
}
return domParserInstance;
}
/**
* Add event listener to window
* @param {string} event - Event name
* @param {EventListenerOrEventListenerObject} handler - Event handler
* @param {object} options - Event options
*/
static addWindowEventListener(event, handler, options2) {
if (Environment.isBrowser()) {
window.addEventListener(event, handler, options2);
}
}
/**
* Remove event listener from window
* @param {string} event - Event name
* @param {EventListenerOrEventListenerObject} handler - Event handler
* @param {object} options - Event options
*/
static removeWindowEventListener(event, handler, options2) {
if (Environment.isBrowser()) {
window.removeEventListener(event, handler, options2);
}
}
/**
* Request animation frame
* @param {FrameRequestCallback} callback - Callback function
* @returns {number|null}
*/
static requestAnimationFrame(callback) {
if (Environment.isBrowser()) {
return window.requestAnimationFrame(callback);
}
callback(0);
return null;
}
/**
* Cancel animation frame
* @param {number} id - Animation frame ID
*/
static cancelAnimationFrame(id) {
if (Environment.isBrowser() && id) {
window.cancelAnimationFrame(id);
}
}
/**
* Check if element exists
* @param {Element} element - Element to check
* @returns {boolean}
*/
static elementExists(element) {
if (!element) return false;
if (Environment.isSSR()) {
return element._ssrMode === true || element.nodeName !== void 0;
}
return element.getRootNode ? element.getRootNode({ composed: true }) === document || element.isConnected : false;
}
/**
* Get window object (or null in SSR)
* @returns {Window|null}
*/
static getWindow() {
return Environment.isBrowser() ? window : null;
}
/**
* Get document object (or null in SSR)
* @returns {Document|null}
*/
static getDocument() {
return Environment.isBrowser() ? document : null;
}
/**
* Get the shim instance (for testing purposes)
* @returns {SSRDOMShim|null}
*/
static _getShim() {
return shim;
}
/**
* Reset the shim instance (for testing purposes)
*/
static _resetShim() {
shim = null;
xmlSerializerInstance = null;
domParserInstance = null;
}
}
let Utils$1 = class Utils {
/**
* @param {*} item
*/
static isObject(item) {
return item && typeof item === "object" && !Array.isArray(item);
}
// Type checking that works across different window objects
/**
* @param {string} type
* @param {string} val
*/
static is(type, val) {
return Object.prototype.toString.call(val) === "[object " + type + "]";
}
static isSafari() {
return Environment.isBrowser() && /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
}
// to extend defaults with user options
// credit: http://stackoverflow.com/questions/27936772/deep-object-merging-in-es6-es7#answer-34749873
/**
* @param {any} target
* @param {any} source
*/
static extend(target, source) {
const output = Object.assign({}, target);
if (this.isObject(target) && this.isObject(source)) {
Object.keys(source).forEach((key) => {
if (this.isObject(source[key])) {
if (!(key in target)) {
Object.assign(output, {
[key]: source[key]
});
} else {
output[key] = this.extend(target[key], source[key]);
}
} else {
Object.assign(output, {
[key]: source[key]
});
}
});
}
return output;
}
/**
* @param {any[]} arrToExtend
* @param {any} resultArr
*/
static extendArray(arrToExtend, resultArr) {
const extendedArr = [];
arrToExtend.map((item) => {
extendedArr.push(Utils.extend(resultArr, item));
});
arrToExtend = extendedArr;
return arrToExtend;
}
// If month counter exceeds 12, it starts again from 1
/**
* @param {number} month
*/
static monthMod(month) {
return month % 12;
}
/**
* clone object with optional shallow copy for performance
* @param {*} source - Source object to clone
* @param {WeakMap<any, any>} visited - Circular reference tracker
* @param {boolean} shallow - If true, performs shallow copy (default: false)
* @returns {*} Cloned object
*/
static clone(source, visited = /* @__PURE__ */ new WeakMap(), shallow = false) {
if (source === null || typeof source !== "object") {
return source;
}
if (visited.has(source)) {
return visited.get(source);
}
let cloneResult;
if (Array.isArray(source)) {
if (shallow) {
cloneResult = source.slice();
} else {
cloneResult = [];
visited.set(source, cloneResult);
for (let i = 0; i < source.length; i++) {
cloneResult[i] = this.clone(source[i], visited, false);
}
}
} else if (source instanceof Date) {
cloneResult = new Date(source.getTime());
} else {
if (shallow) {
cloneResult = Object.assign({}, source);
} else {
cloneResult = {};
visited.set(source, cloneResult);
for (const prop in source) {
if (Object.prototype.hasOwnProperty.call(source, prop)) {
cloneResult[prop] = this.clone(
/** @type {Record<string,any>} */
source[prop],
visited,
false
);
}
}
}
}
return cloneResult;
}
/**
* Shallow clone for performance when deep clone isn't needed
* @param {*} source - Source to clone
* @returns {*} Shallow cloned object
*/
static shallowClone(source) {
if (source === null || typeof source !== "object") {
return source;
}
if (Array.isArray(source)) {
return source.slice();
}
return Object.assign({}, source);
}
/**
* Fast shallow equality check for objects
* @param {Object} obj1 - First object
* @param {Object} obj2 - Second object
* @returns {boolean} True if shallowly equal
*/
static shallowEqual(obj1, obj2) {
if (obj1 === obj2) return true;
if (!obj1 || !obj2) return false;
if (typeof obj1 !== "object" || typeof obj2 !== "object") {
return obj1 === obj2;
}
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
for (const key of keys1) {
if (
/** @type {Record<string,any>} */
obj1[key] !== /** @type {Record<string,any>} */
obj2[key]
)
return false;
}
return true;
}
/**
* @param {number} x
*/
static log10(x) {
return Math.log(x) / Math.LN10;
}
/**
* @param {number} x
*/
static roundToBase10(x) {
return Math.pow(10, Math.floor(Math.log10(x)));
}
/**
* @param {number} x
* @param {number} base
*/
static roundToBase(x, base) {
return Math.pow(base, Math.floor(Math.log(x) / Math.log(base)));
}
/**
* @param {any} val
*/
static parseNumber(val) {
if (typeof val === "number" || val === null) return val;
return parseFloat(val);
}
/**
* @param {number} num
*/
static stripNumber(num, precision = 2) {
return Number.isInteger(num) ? num : parseFloat(num.toPrecision(precision));
}
static randomId() {
return (Math.random() + 1).toString(36).substring(4);
}
/**
* @param {number} num
*/
static noExponents(num) {
if (num.toString().includes("e")) {
return Math.round(num);
}
return num;
}
/**
* @param {any} element
*/
static elementExists(element) {
if (!element || !element.isConnected) {
return false;
}
return true;
}
/**
* detects if an element is inside a Shadow DOM
* @param {any} el
*/
static isInShadowDOM(el) {
if (!el || !el.getRootNode) {
return false;
}
const rootNode = el.getRootNode();
return rootNode && rootNode !== document && Utils.is("ShadowRoot", rootNode);
}
/**
* gets the shadow root host element
* @param {any} el
*/
static getShadowRootHost(el) {
if (!Utils.isInShadowDOM(el)) {
return null;
}
const rootNode = el.getRootNode();
return rootNode.host || null;
}
/**
* @param {any} el
*/
static getDimensions(el) {
if (!el) return [0, 0];
if (Environment.isSSR()) {
return [el._ssrWidth || 400, el._ssrHeight || 300];
}
let computedStyle;
try {
computedStyle = getComputedStyle(el, null);
} catch (e) {
return [el.clientWidth || 0, el.clientHeight || 0];
}
let elementWidth = el.clientWidth;
let elementHeight = el.clientHeight;
if (!elementWidth || !elementHeight) {
const rect = el.getBoundingClientRect();
elementWidth = elementWidth || rect.width;
elementHeight = elementHeight || rect.height;
}
elementHeight -= parseFloat(computedStyle.paddingTop) + parseFloat(computedStyle.paddingBottom);
elementWidth -= parseFloat(computedStyle.paddingLeft) + parseFloat(computedStyle.paddingRight);
return [elementWidth, elementHeight];
}
/**
* @returns {any}
* @param {any} element
*/
static getBoundingClientRect(element) {
if (!element) {
return {
top: 0,
right: 0,
bottom: 0,
left: 0,
width: 0,
height: 0,
x: 0,
y: 0
};
}
if (Environment.isSSR()) {
return BrowserAPIs.getBoundingClientRect(element);
}
const rect = element.getBoundingClientRect();
return {
top: rect.top,
right: rect.right,
bottom: rect.bottom,
left: rect.left,
width: element.clientWidth,
height: element.clientHeight,
x: rect.left,
y: rect.top
};
}
/**
* @param {any[]} arr
*/
static getLargestStringFromArr(arr) {
return arr.reduce((a, b) => {
if (Array.isArray(b)) {
b = b.reduce((aa, bb) => aa.length > bb.length ? aa : bb);
}
return a.length > b.length ? a : b;
}, 0);
}
// http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb#answer-12342275
static hexToRgba(hex = "#999999", opacity = 0.6) {
if (hex.substring(0, 1) !== "#") {
hex = "#999999";
}
const hexStr = hex.replace("#", "");
const h = hexStr.match(new RegExp("(.{" + hexStr.length / 3 + "})", "g")) || [];
for (let i = 0; i < h.length; i++) {
h[i] = parseInt(h[i].length === 1 ? h[i] + h[i] : h[i], 16);
}
if (typeof opacity !== "undefined") h.push(opacity);
return "rgba(" + h.join(",") + ")";
}
/**
* @param {string} rgba
*/
static getOpacityFromRGBA(rgba) {
return parseFloat(rgba.replace(/^.*,(.+)\)/, "$1"));
}
/**
* Parse a #RGB or #RRGGBB hex colour string into [r,g,b] in 0–255.
* Returns null for invalid input. Used by getContrastRatio.
* @param {string} hex
* @returns {[number, number, number] | null}
*/
static parseHex(hex) {
if (typeof hex !== "string") return null;
let h = hex.trim().replace("#", "");
if (h.length === 3) {
h = h.split("").map((c) => c + c).join("");
}
if (!/^[0-9a-fA-F]{6}$/.test(h)) return null;
return [
parseInt(h.slice(0, 2), 16),
parseInt(h.slice(2, 4), 16),
parseInt(h.slice(4, 6), 16)
];
}
/**
* Relative luminance per WCAG 2.x (https://www.w3.org/TR/WCAG22/#dfn-relative-luminance).
* @param {[number, number, number]} rgb 0–255 sRGB triplet
* @returns {number} 0.0–1.0
*/
static relativeLuminance([r, g, b]) {
const channel = (c) => {
const v = c / 255;
return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
};
return 0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b);
}
/**
* WCAG contrast ratio between two hex colours. Returns 0 for invalid input.
* Range: 1 (identical) … 21 (#000 vs #fff).
* WCAG AA requires ≥ 4.5 for normal text and ≥ 3.0 for large text / UI components.
* @param {string} hex1
* @param {string} hex2
* @returns {number}
*/
static getContrastRatio(hex1, hex2) {
const rgb1 = Utils.parseHex(hex1);
const rgb2 = Utils.parseHex(hex2);
if (!rgb1 || !rgb2) return 0;
const l1 = Utils.relativeLuminance(rgb1);
const l2 = Utils.relativeLuminance(rgb2);
const lighter = Math.max(l1, l2);
const darker = Math.min(l1, l2);
return (lighter + 0.05) / (darker + 0.05);
}
/**
* @param {any} rgb
*/
static rgb2hex(rgb) {
rgb = rgb.match(
/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i
);
return rgb && rgb.length === 4 ? "#" + ("0" + parseInt(rgb[1], 10).toString(16)).slice(-2) + ("0" + parseInt(rgb[2], 10).toString(16)).slice(-2) + ("0" + parseInt(rgb[3], 10).toString(16)).slice(-2) : "";
}
/**
* @param {number} percent
* @param {string} color
*/
shadeRGBColor(percent, color) {
const f = color.split(","), t = percent < 0 ? 0 : 255, p = percent < 0 ? percent * -1 : percent, R = parseInt(f[0].slice(4), 10), G = parseInt(f[1], 10), B = parseInt(f[2], 10);
return "rgb(" + (Math.round((t - R) * p) + R) + "," + (Math.round((t - G) * p) + G) + "," + (Math.round((t - B) * p) + B) + ")";
}
/**
* @param {number} percent
* @param {string} color
*/
shadeHexColor(percent, color) {
const f = parseInt(color.slice(1), 16), t = percent < 0 ? 0 : 255, p = percent < 0 ? percent * -1 : percent, R = f >> 16, G = f >> 8 & 255, B = f & 255;
return "#" + (16777216 + (Math.round((t - R) * p) + R) * 65536 + (Math.round((t - G) * p) + G) * 256 + (Math.round((t - B) * p) + B)).toString(16).slice(1);
}
// beautiful color shading blending code
// http://stackoverflow.com/questions/5560248/programmatically-lighten-or-darken-a-hex-color-or-rgb-and-blend-colors
/**
* @param {number} p
* @param {string} color
*/
shadeColor(p, color) {
if (Utils.isColorHex(color)) {
return this.shadeHexColor(p, color);
} else {
return this.shadeRGBColor(p, color);
}
}
/**
* @param {string} color
*/
static isColorHex(color) {
return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)|(^#[0-9A-F]{8}$)/i.test(color);
}
/**
* @param {string} color
*/
static isCSSVariable(color) {
if (typeof color !== "string") return false;
const value = color.trim();
return value.startsWith("var(") && value.endsWith(")");
}
/**
* @param {string} color
*/
static getThemeColor(color) {
if (!Utils.isCSSVariable(color)) return color;
if (Environment.isSSR()) return color;
const tempElem = document.createElement("div");
tempElem.style.cssText = "position:fixed; left: -9999px; visibility:hidden;";
tempElem.style.color = color;
document.body.appendChild(tempElem);
let computedColor;
try {
computedColor = window.getComputedStyle(tempElem).color;
} finally {
if (tempElem.parentNode) {
tempElem.parentNode.removeChild(tempElem);
}
}
return computedColor;
}
/**
* @param {string} color
* @param {number} opacity
*/
static applyOpacityToColor(color, opacity) {
const value = Number(opacity);
if (!Number.isFinite(value)) return color;
if (value <= 0) return "transparent";
if (value >= 1) return color;
const percent = Math.round(value * 100);
return `color-mix(in srgb, ${color} ${percent}%, transparent)`;
}
/**
* @param {number} size
* @param {number} dataPointsLen
*/
static getPolygonPos(size, dataPointsLen) {
const dotsArray = [];
const angle = Math.PI * 2 / dataPointsLen;
for (let i = 0; i < dataPointsLen; i++) {
const curPos = {};
curPos.x = size * Math.sin(i * angle);
curPos.y = -size * Math.cos(i * angle);
dotsArray.push(curPos);
}
return dotsArray;
}
/**
* @param {number} centerX
* @param {number} centerY
* @param {number} radius
* @param {number} angleInDegrees
*/
static polarToCartesian(centerX, centerY, radius, angleInDegrees) {
const angleInRadians = (angleInDegrees - 90) * Math.PI / 180;
return {
x: centerX + radius * Math.cos(angleInRadians),
y: centerY + radius * Math.sin(angleInRadians)
};
}
/**
* @param {string} str
*/
static escapeString(str, escapeWith = "x") {
let newStr = str.toString().slice();
newStr = newStr.replace(/[` ~!@#$%^&*()|+=?;:'",.<>{}[\]\\/]/gi, escapeWith);
return newStr;
}
/**
* @param {number} val
*/
static negToZero(val) {
return val < 0 ? 0 : val;
}
/**
* @param {any[]} arr
* @param {number} old_index
* @param {number} new_index
*/
static moveIndexInArray(arr, old_index, new_index) {
if (new_index >= arr.length) {
let k = new_index - arr.length + 1;
while (k--) {
arr.push(void 0);
}
}
arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
return arr;
}
/**
* @param {string} s
*/
static extractNumber(s) {
return parseFloat(s.replace(/[^\d.]*/g, ""));
}
/**
* @param {any} el
* @param {string} cls
*/
static findAncestor(el, cls) {
while ((el = el.parentElement) && !el.classList.contains(cls)) ;
return el;
}
/**
* @param {any} el
* @param {Record<string, any>} styles
*/
static setELstyles(el, styles) {
for (const key in styles) {
if (Object.prototype.hasOwnProperty.call(styles, key)) {
el.style.key = styles[key];
}
}
}
// prevents JS prevision errors when adding
/**
* @param {number} a
* @param {number} b
*/
static preciseAddition(a, b) {
const aDecimals = (String(a).split(".")[1] || "").length;
const bDecimals = (String(b).split(".")[1] || "").length;
const factor = Math.pow(10, Math.max(aDecimals, bDecimals));
return (Math.round(a * factor) + Math.round(b * factor)) / factor;
}
/**
* @param {any} value
*/
static isNumber(value) {
return !isNaN(value) && parseFloat(String(Number(value))) === value && !isNaN(parseInt(value, 10));
}
/**
* @param {number} n
*/
static isFloat(n) {
return Number(n) === n && n % 1 !== 0;
}
static isMsEdge() {
if (Environment.isSSR()) return false;
const ua = window.navigator.userAgent;
const edge = ua.indexOf("Edge/");
if (edge > 0) {
return parseInt(ua.substring(edge + 5, ua.indexOf(".", edge)), 10);
}
return false;
}
//
// Find the Greatest Common Divisor of two numbers
//
/**
* @param {number} a
* @param {number} b
*/
static getGCD(a, b, p = 7) {
let factor = Math.pow(10, p - Math.floor(Math.log10(Math.max(a, b))));
if (factor > 1) {
a = Math.round(Math.abs(a) * factor);
b = Math.round(Math.abs(b) * factor);
} else {
factor = 1;
}
while (b) {
const t = b;
b = a % b;
a = t;
}
return a / factor;
}
/**
* @param {number} n
*/
static getPrimeFactors(n) {
const factors = [];
let divisor = 2;
while (n >= 2) {
if (n % divisor == 0) {
factors.push(divisor);
n = n / divisor;
} else {
divisor++;
}
}
return factors;
}
/**
* @param {number} a
* @param {number} b
*/
static mod(a, b, p = 7) {
const big = Math.pow(10, p - Math.floor(Math.log10(Math.max(a, b))));
a = Math.round(Math.abs(a) * big);
b = Math.round(Math.abs(b) * big);
return a % b / big;
}
};
class DateTime {
/**
* @param {import('../types/internal').ChartStateW} w
*/
constructor(w) {
this.w = w;
this.months31 = [1, 3, 5, 7, 8, 10, 12];
this.months30 = [2, 4, 6, 9, 11];
this.daysCntOfYear = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
}
/**
* @param {any} date
*/
isValidDate(date) {
if (typeof date === "number") {
return false;
}
return !isNaN(this.parseDate(date));
}
/**
* @param {any} dateStr
*/
getTimeStamp(dateStr) {
if (!Date.parse(dateStr)) {
return dateStr;
}
const utc = this.w.config.xaxis.labels.datetimeUTC;
return !utc ? new Date(dateStr).getTime() : new Date(new Date(dateStr).toISOString().substr(0, 25)).getTime();
}
/**
* @param {any} timestamp
*/
getDate(timestamp) {
const utc = this.w.config.xaxis.labels.datetimeUTC;
return utc ? new Date(new Date(timestamp).toUTCString()) : new Date(timestamp);
}
/**
* @param {string} dateStr
*/
parseDate(dateStr) {
const parsed = Date.parse(dateStr);
if (!isNaN(parsed)) {
return this.getTimeStamp(dateStr);
}
let output = Date.parse(dateStr.replace(/-/g, "/").replace(/[a-z]+/gi, " "));
output = this.getTimeStamp(output);
return output;
}
// This fixes the difference of x-axis labels between chrome/safari
// Fixes #1726, #1544, #1485, #1255
/**
* @param {string} dateStr
*/
parseDateWithTimezone(dateStr) {
return Date.parse(dateStr.replace(/-/g, "/").replace(/[a-z]+/gi, " "));
}
// http://stackoverflow.com/questions/14638018/current-time-formatting-with-javascript#answer-14638191
/**
* @param {Date} date
* @param {string} format
*/
formatDate(date, format) {
const locale = this.w.globals.locale;
const utc = this.w.config.xaxis.labels.datetimeUTC;
const MMMM = ["\0", ...locale.months];
const MMM = ["", ...locale.shortMonths];
const dddd = ["", ...locale.days];
const ddd = ["", ...locale.shortDays];
function ii(i, len = 2) {
let s2 = i + "";
while (s2.length < len) s2 = "0" + s2;
return s2;
}
const y = utc ? date.getUTCFullYear() : date.getFullYear();
format = format.replace(/(^|[^\\])yyyy+/g, "$1" + y);
format = format.replace(/(^|[^\\])yy/g, "$1" + y.toString().substr(2, 2));
format = format.replace(/(^|[^\\])y/g, "$1" + y);
const M = (utc ? date.getUTCMonth() : date.getMonth()) + 1;
format = format.replace(/(^|[^\\])MMMM+/g, "$1" + MMMM[0]);
format = format.replace(/(^|[^\\])MMM/g, "$1" + MMM[0]);
format = format.replace(/(^|[^\\])MM/g, "$1" + ii(M));
format = format.replace(/(^|[^\\])M/g, "$1" + M);
const d = utc ? date.getUTCDate() : date.getDate();
format = format.replace(/(^|[^\\])dddd+/g, "$1" + dddd[0]);
format = format.replace(/(^|[^\\])ddd/g, "$1" + ddd[0]);
format = format.replace(/(^|[^\\])dd/g, "$1" + ii(d));
format = format.replace(/(^|[^\\])d/g, "$1" + d);
const H = utc ? date.getUTCHours() : date.getHours();
format = format.replace(/(^|[^\\])HH+/g, "$1" + ii(H));
format = format.replace(/(^|[^\\])H/g, "$1" + H);
const h = H > 12 ? H - 12 : H === 0 ? 12 : H;
format = format.replace(/(^|[^\\])hh+/g, "$1" + ii(h));
format = format.replace(/(^|[^\\])h/g, "$1" + h);
const m = utc ? date.getUTCMinutes() : date.getMinutes();
format = format.replace(/(^|[^\\])mm+/g, "$1" + ii(m));
format = format.replace(/(^|[^\\])m/g, "$1" + m);
const s = utc ? date.getUTCSeconds() : date.getSeconds();
format = format.replace(/(^|[^\\])ss+/g, "$1" + ii(s));
format = format.replace(/(^|[^\\])s/g, "$1" + s);
let f = utc ? date.getUTCMilliseconds() : date.getMilliseconds();
format = format.replace(/(^|[^\\])fff+/g, "$1" + ii(f, 3));
f = Math.round(f / 10);
format = format.replace(/(^|[^\\])ff/g, "$1" + ii(f));
f = Math.round(f / 10);
format = format.replace(/(^|[^\\])f/g, "$1" + f);
const T = H < 12 ? "AM" : "PM";
format = format.replace(/(^|[^\\])TT+/g, "$1" + T);
format = format.replace(/(^|[^\\])T/g, "$1" + T.charAt(0));
const t = T.toLowerCase();
format = format.replace(/(^|[^\\])tt+/g, "$1" + t);
format = format.replace(/(^|[^\\])t/g, "$1" + t.charAt(0));
let tz = -date.getTimezoneOffset();
let K = utc || !tz ? "Z" : tz > 0 ? "+" : "-";
if (!utc) {
tz = Math.abs(tz);
const tzHrs = Math.floor(tz / 60);
const tzMin = tz % 60;
K += ii(tzHrs) + ":" + ii(tzMin);
}
format = format.replace(/(^|[^\\])K/g, "$1" + K);
const day = (utc ? date.getUTCDay() : date.getDay()) + 1;
format = format.replace(new RegExp(dddd[0], "g"), dddd[day]);
format = format.replace(new RegExp(ddd[0], "g"), ddd[day]);
format = format.replace(new RegExp(MMMM[0], "g"), MMMM[M]);
format = format.replace(new RegExp(MMM[0], "g"), MMM[M]);
format = format.replace(/\\(.)/g, "$1");
return format;
}
/**
* @param {number} minX
* @param {number} maxX
*/
getTimeUnitsfromTimestamp(minX, maxX) {
const w = this.w;
if (w.config.xaxis.min !== void 0) {
minX = w.config.xaxis.min;
}
if (w.config.xaxis.max !== void 0) {
maxX = w.config.xaxis.max;
}
const tsMin = this.getDate(minX);
const tsMax = this.getDate(maxX);
const minD = this.formatDate(tsMin, "yyyy MM dd HH mm ss fff").split(" ");
const maxD = this.formatDate(tsMax, "yyyy MM dd HH mm ss fff").split(" ");
return {
minMillisecond: parseInt(minD[6], 10),
maxMillisecond: parseInt(maxD[6], 10),
minSecond: parseInt(minD[5], 10),
maxSecond: parseInt(maxD[5], 10),
minMinute: parseInt(minD[4], 10),
maxMinute: parseInt(maxD[4], 10),
minHour: parseInt(minD[3], 10),
maxHour: parseInt(maxD[3], 10),
minDate: parseInt(minD[2], 10),
maxDate: parseInt(maxD[2], 10),
minMonth: parseInt(minD[1], 10) - 1,
maxMonth: parseInt(maxD[1], 10) - 1,
minYear: parseInt(minD[0], 10),
maxYear: parseInt(maxD[0], 10)
};
}
/**
* @param {number} year
*/
isLeapYear(year) {
return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
}
/**
* @param {number} month
* @param {number} year
* @param {number} subtract
*/
calculcateLastDaysOfMonth(month, year, subtract) {
const days = this.determineDaysOfMonths(month, year);
return days - subtract;
}
/**
* @param {number} year
*/
determineDaysOfYear(year) {
let days = 365;
if (this.isLeapYear(year)) {
days = 366;
}
return days;
}
/**
* @param {number} year
* @param {number} month
* @param {number} date
*/
determineRemainingDaysOfYear(year, month, date) {
let dayOfYear = this.daysCntOfYear[month] + date;
if (month > 1 && this.isLeapYear(year)) dayOfYear++;
return dayOfYear;
}
/**
* @param {number} month
* @param {number} year
*/
determineDaysOfMonths(month, year) {
let days = 30;
month = Utils$1.monthMod(month);
switch (true) {
case this.months30.indexOf(month) > -1:
if (month === 2) {
if (this.isLeapYear(year)) {
days = 29;
} else {
days = 28;
}
}
break;
case this.months31.indexOf(month) > -1:
days = 31;
break;
default:
days = 31;
break;
}
return days;
}
}
class Formatters {
/**
* @param {import('../types/internal').ChartStateW} w
*/
constructor(w) {
this.w = w;
this.tooltipKeyFormat = "dd MMM";
}
/**
* @param {Function} fn
* @param {any} val
* @param {any} timestamp
* @param {any} _opts
*/
xLabelFormat(fn, val, timestamp, _opts) {
const w = this.w;
if (w.config.xaxis.type === "datetime") {
if (w.config.xaxis.labels.formatter === void 0) {
if (w.config.tooltip.x.formatter === void 0) {
const datetimeObj = new DateTime(this.w);
return datetimeObj.formatDate(
datetimeObj.getDate(val),
w.config.tooltip.x.format
);
}
}
}
return fn(val, timestamp, _opts);
}
/**
* @param {any} val
*/
defaultGeneralFormatter(val) {
if (Array.isArray(val)) {
return val.map((v) => {
return v;
});
} else {
return val;
}
}
/**
* @param {any} v
* @param {ApexYAxis} yaxe
*/
defaultYFormatter(v, yaxe) {
const w = this.w;
if (Utils$1.isNumber(v)) {
if (w.globals.yValueDecimal !== 0) {
v = v.toFixed(
yaxe.decimalsInFloat !== void 0 ? yaxe.decimalsInFloat : w.globals.yValueDecimal
);
} else {
const f = v.toFixed(0);
v = Number(f) === v ? f : v.toFixed(1);
}
}
return v;
}
setLabelFormatters() {
const w = this.w;
const fmt = w.formatters;
fmt.xaxisTooltipFormatter = (val) => {
return this.defaultGeneralFormatter(val);
};
fmt.ttKeyFormatter = (val) => {
return this.defaultGeneralFormatter(val);
};
fmt.ttZFormatter = (val) => {
return val;
};
fmt.legendFormatter = (val) => {
return this.defaultGeneralFormatter(val);
};
if (w.config.xaxis.labels.formatter !== void 0) {
fmt.xLabelFormatter = w.config.xaxis.labels.formatter;
} else {
fmt.xLabelFormatter = (val) => {
if (Utils$1.isNumber(val)) {
if (!w.config.xaxis.convertedCatToNumeric && w.config.xaxis.type === "numeric") {
if (Utils$1.isNumber(w.config.xaxis.decimalsInFloat)) {
return val.toFixed(w.config.xaxis.decimalsInFloat);
} else {
const diff = w.globals.maxX - w.globals.minX;
if (diff > 0 && diff < 100) {
return val.toFixed(1);
}
return val.toFixed(0);
}
}
if (w.globals.isBarHorizontal) {
const range = w.globals.maxY - /** @type {any} */
w.globals.minYArr;
if (range < 4) {
return val.toFixed(1);
}
}
return val.toFixed(0);
}
return val;
};
}
if (typeof w.config.tooltip.x.formatter === "function") {
fmt.ttKeyFormatter = w.config.tooltip.x.formatter;
} else {
fmt.ttKeyFormatter = fmt.xLabelFormatter;
}
if (typeof w.config.xaxis.tooltip.formatter === "function") {
fmt.xaxisTooltipFormatter = w.config.xaxis.tooltip.formatter;
}
if (Array.isArray(w.config.tooltip.y)) {
fmt.ttVal = w.config.tooltip.y;
} else {
if (w.config.tooltip.y.formatter !== void 0) {
fmt.ttVal = w.config.tooltip.y;
}
}
if (w.config.tooltip.z.formatter !== void 0) {
fmt.ttZFormatter = w.config.tooltip.z.formatter;
}
if (w.config.legend.formatter !== void 0) {
fmt.legendFormatter = w.config.legend.formatter;
}
fmt.yLabelFormatters = [];
w.config.yaxis.forEach((yaxe, i) => {
if (yaxe.labels.formatter !== void 0) {
fmt.yLabelFormatters[i] = yaxe.labels.formatter;
} else {
fmt.yLabelFormatters[i] = (val) => {
if (!w.globals.xyCharts) return val;
if (Array.isArray(val)) {
return val.map((v) => {
return this.defaultYFormatter(v, yaxe);
});
} else {
return this.defaultYFormatter(val, yaxe);
}
};
}
});
return w.globals;
}
heatmapLabelFormatters() {
const w = this.w;
if (w.config.chart.type === "heatmap") {
w.globals.yAxisScale[0].result = /** @type {any} */
w.seriesData.seriesNames.slice();
const longest = (
/** @type {any} */
w.seriesData.seriesNames.reduce(
(a, b) => a.length > b.length ? a : b,
0
)
);
w.globals.yAxisScale[0].niceMax = longest;
w.globals.yAxisScale[0].niceMin = longest;
}
}
}
const getRangeValues = ({
isTimeline,
seriesIndex,
dataPointIndex,
y1,
y2,
w
}) => {
var _a;
let start = w.rangeData.seriesRangeStart[seriesIndex][dataPointIndex];
let end = w.rangeData.seriesRangeEnd[seriesIndex][dataPointIndex];
let ylabel = w.labelData.labels[dataPointIndex];
let seriesName = w.config.series[seriesIndex].name ? w.config.series[seriesIndex].name : "";
const yLbFormatter = w.formatters.ttKeyFormatter;
const yLbTitleFormatter = w.config.tooltip.y.title.formatter;
const opts = {
w,
seriesIndex,
dataPointIndex,
start,
end
};
if (typeof yLbTitleFormatter === "function") {
seriesName = yLbTitleFormatter(seriesName, opts);
}
if ((_a = w.config.series[seriesIndex].data[dataPointIndex]) == null ? void 0 : _a.x) {
ylabel = w.config.series[seriesIndex].data[dataPointIndex].x;
}
if (!isTimeline) {
if (w.config.xaxis.type === "datetime") {
const xFormat = new Formatters(w);
ylabel = xFormat.xLabelFormat(
w.formatters.ttKeyFormatter,
ylabel,
ylabel,
{
i: void 0,
dateFormatter: new DateTime(w).formatDate,
w
}
);
}
}
if (typeof yLbFormatter === "function") {
ylabel = yLbFormatter(ylabel, opts);
}
if (Number.isFinite(y1) && Number.isFinite(y2)) {
start = y1;
end = y2;
}
let startVal = "";
let endVal = "";
const color = w.globals.colors[seriesIndex];
if (w.config.tooltip.x.formatter === void 0) {
if (w.config.xaxis.type === "datetime") {
const datetimeObj = new DateTime(w);
startVal = datetimeObj.formatDate(
datetimeObj.getDate(start),
w.config.tooltip.x.format
);
endVal = datetimeObj.formatDate(
datetimeObj.getDate(end),
w.config.tooltip.x.format
);
} else {
startVal = start;
endVal = end;
}
} else {
startVal = w.config.tooltip.x.formatter(start);
endVal = w.config.tooltip.x.formatter(end);
}
return { start, end, startVal, endVal, ylabel, color, seriesName };
};
const buildRangeTooltipHTML = (opts) => {
let { color, seriesName, ylabel, start, end, seriesIndex, dataPointIndex } = opts;
const formatter = opts.w.globals.tooltip.tooltipLabels.getFormatters(seriesIndex);
start = formatter.yLbFormatter(start);
end = formatter.yLbFormatter(end);
const val = formatter.yLbFormatter(
opts.w.seriesData.series[seriesIndex][dataPointIndex]
);
let valueHTML = "";
const rangeValues = `<span class="value start-value">
${start}
</span> <span class="separator">-</span> <span class="value end-value">
${end}
</span>`;
if (opts.w.globals.comboCharts) {
if (opts.w.config.series[seriesIndex].type === "rangeArea" || opts.w.config.series[seriesIndex].type === "rangeBar") {
valueHTML = rangeValues;
} else {
valueHTML = `<span>${val}</span>`;
}
} else {
valueHTML = rangeValues;
}
return '<div class="apexcharts-tooltip-rangebar"><div> <span class="series-name" style="color: ' + color + '">' + (seriesName ? seriesName : "") + '</span></div><div> <span class="category">' + ylabel + ": </span> " + valueHTML + " </div></div>";
};
class Defaults {
/**
* @param {Record<string, any>} opts
*/
constructor(opts) {
this.opts = opts;
}
hideYAxis() {
this.opts.yaxis[0].show = false;
this.opts.yaxis[0].title.text = "";
this.opts.yaxis[0].axisBorder.show = false;
this.opts.yaxis[0].axisTicks.show = false;
this.opts.yaxis[0].floating = true;
}
line() {
return {
dataLabels: {
enabled: false
},
stroke: {
width: 5,
curve: "straight"
},
markers: {
size: 0,
hover: {
sizeOffset: 6
}
},
xaxis: {
crosshairs: {
width: 1
}
}
};
}
/**
* @param {Record<string, any>} defaults
*/
sparkline(defaults) {
this.hideYAxis();
const ret = {
grid: {
show: false,
padding: {
left: 0,
right: 0,
top: 0,
bottom: 0
}
},
legend: {
show: false
},
xaxis: {
labels: {
show: false
},
tooltip: {
enabled: false
},
axisBorder: {
show: false
},
axisTicks: {
show: false
}
},
chart: {
toolbar: {
show: false
},
zoom: {
enabled: false
}
},
dataLabels: {
enabled: false
}
};
return Utils$1.extend(defaults, ret);
}
slope() {
this.hideYAxis();
return {
chart: {
toolbar: {
show: false
},
zoom: {
enabled: false
}
},
dataLabels: {
enabled: true,
/**
* @param {any} val
* @param {Record<string, any>} opts
*/
formatter(val, opts) {
const seriesName = opts.w.config.series[opts.seriesIndex].name;
return val !== null ? seriesName + ": " + val : "";
},
background: {
enabled: false
},
offsetX: -5
},
grid: {
xaxis: {
lines: {
show: true
}
},
yaxis: {
lines: {
show: false
}
}
},
xaxis: {
position: "top",
labels: {
style: {
fontSize: 14,
fontWeight: 900
}
},
tooltip: {
enabled: false
},
crosshairs: {
show: false
}
},
markers: {
size: 8,
hover: {
sizeOffset: 1
}
},
legend: {
show: false
},
tooltip: {
shared: false,
intersect: true,
followCursor: true
},
stroke: {
width: 5,
curve: "straight"
}
};
}
bar() {
return {
chart: {
stacked: false
},
plotOptions: {
bar: {
dataLabels: {
position: "center"
}
}
},
dataLabels: {
style: {
colors: ["#fff"]
},
background: {
enabled: false
}
},
stroke: {
width: 0,
lineCap: "square"
},
fill: {
opacity: 0.85
},
legend: {
markers: {
shape: "square"
}
},
tooltip: {
shared: false,
intersect: true
},
xaxis: {
tooltip: {
enabled: false
},
tickPlacement: "between",
crosshairs: {
width: "barWidth",
position: "back",
fill: {
type: "gradient"
},
dropShadow: {
enabled: false
},
stroke: {
width: 0
}
}
}
};
}
funnel() {
this.hideYAxis();
return __spreadProps(__spreadValues({}, this.bar()), {
chart: {
animations: {
speed: 800,
animateGradually: {
enabled: false
}
}
},
plotOptions: {
bar: {
horizontal: true,
borderRadiusApplication: "around",
borderRadius: 0,
dataLabels: {
position: "center"
}
}
},
grid: {
show: false,
padding: {
left: 0,
right: 0
}
},
xaxis: {
labels: {
show: false
},
tooltip: {
enabled: false
},
axisBorder: {
show: false
},
axisTicks: {
show: false
}
}
});
}
candlestick() {
return {
stroke: {
width: 1
},
fill: {
opacity: 1
},
dataLabels: {
enabled: false
},
tooltip: {
shared: true,
custom: ({ seriesIndex, dataPointIndex, w }) => {
return this._getBoxTooltip(
w,
seriesIndex,
dataPointIndex,
["Open", "High", "", "Low", "Close"],
"candlestick"
);
}
},
states: {
active: {
filter: {
type: "none"
}
}
},
xaxis: {
crosshairs: {
width: 1
}
}
};
}
boxPlot() {
return {
chart: {
animations: {
dynamicAnimation: {
enabled: false
}
}
},
stroke: {
width: 1,
colors: ["#24292e"]
},
dataLabels: {
enabled: false
},
tooltip: {
shared: true,
custom: ({ seriesIndex, dataPointIndex, w }) => {
return this._getBoxTooltip(
w,
seriesIndex,
dataPointIndex,
["Minimum", "Q1", "Median", "Q3", "Maximum"],
"boxPlot"
);
}
},
markers: {
size: 7,
strokeWidth: 1,
strokeColors: "#111"
},
xaxis: {
crosshairs: {
width: 1
}
}
};
}
rangeBar() {
const handleTimelineTooltip = (opts) => {
const { color, seriesName, ylabel, startVal, endVal } = getRangeValues(__spreadProps(__spreadValues({}, opts), {
isTimeline: true
}));
return buildRangeTooltipHTML(__spreadProps(__spreadValues({}, opts), {
color,
seriesName,
ylabel,
start: startVal,
end: endVal
}));
};
const handleRangeColumnTooltip = (opts) => {
const { color, seriesName, ylabel, start, end } = getRangeValues(opts);
return buildRangeTooltipHTML(__spreadProps(__spreadValues({}, opts), {
color,
seriesName,
ylabel,
start,
end
}));
};
return {
chart: {
animations: {
animateGradually: false
}
},
stroke: {
width: 0,
lineCap: "square"
},
plotOptions: {
bar: {
borderRadius: 0,
dataLabels: {
position: "center"
}
}
},
dataLabels: {
enabled: false,
/**
* @param {any} val
*/
formatter(val, { seriesIndex, dataPointIndex, w }) {
const getVal = () => {
const start = w.rangeData.seriesRangeStart[seriesIndex][dataPointIndex];
const end = w.rangeData.seriesRangeEnd[seriesIndex][dataPointIndex];
return end - start;
};
if (w.globals.comboCharts) {
if (w.config.series[seriesIndex].type === "rangeBar" || w.config.series[seriesIndex].type === "rangeArea") {
return getVal();
} else {
return val;
}
} else {
return getVal();
}
},
background: {
enabled: false
},
style: {
colors: ["#fff"]
}
},
markers: {
size: 10
},
tooltip: {
shared: false,
followCursor: true,
/**
* @param {Record<string, any>} opts
*/
custom(opts) {
if (opts.w.config.plotOptions && opts.w.config.plotOptions.bar && opts.w.config.plotOptions.bar.horizontal) {
return handleTimelineTooltip(opts);
} else {
return handleRangeColumnTooltip(opts);
}
}
},
xaxis: {
tickPlacement: "between",
tooltip: {
enabled: false
},
crosshairs: {
stroke: {
width: 0
}
}
}
};
}
/**
* @param {Record<string, any>} opts
*/
dumbbell(opts) {
var _a, _b;
if (!((_a = opts.plotOptions.bar) == null ? void 0 : _a.barHeight)) {
opts.plotOptions.bar.barHeight = 2;
}
if (!((_b = opts.plotOptions.bar) == null ? void 0 : _b.columnWidth)) {
opts.plotOptions.bar.columnWidth = 2;
}
return opts;
}
area() {
return {
stroke: {
width: 4,
fill: {
type: "solid",
gradient: {
inverseColors: false,
shade: "light",
type: "vertical",
opacityFrom: 0.65,
opacityTo: 0.5,
stops: [0, 100, 100]
}
}
},
fill: {
type: "gradient",
gradient: {
inverseColors: false,
shade: "light",
type: "vertical",
opacityFrom: 0.65,
opacityTo: 0.5,
stops: [0, 100, 100]
}
},
markers: {
size: 0,
hover: {
sizeOffset: 6
}
},
tooltip: {
followCursor: false
}
};
}
rangeArea() {
const handleRangeAreaTooltip = (opts) => {
const { color, seriesName, ylabel, start, end } = getRangeValues(opts);
return buildRangeTooltipHTML(__spreadProps(__spreadValues({}, opts), {
color,
seriesName,
ylabel,
start,
end
}));
};
return {
stroke: {
curve: "straight",
width: 0
},
fill: {
type: "solid",
opacity: 0.6
},
markers: {
size: 0
},
states: {
hover: {
filter: {
type: "none"
}
},
active: {
filter: {
type: "none"
}
}
},
tooltip: {
intersect: false,
shared: true,
followCursor: true,
/**
* @param {Record<string, any>} opts
*/
custom(opts) {
return handleRangeAreaTooltip(opts);
}
}
};
}
/**
* @param {Record<string, any>} defaults
*/
brush(defaults) {
const ret = {
chart: {
toolbar: {
autoSelected: "selection",
show: false
},
zoom: {
enabled: false
}
},
dataLabels: {
enabled: false
},
stroke: {
width: 1
},
tooltip: {
enabled: false
},
xaxis: {
tooltip: {
enabled: false
}
}
};
return Utils$1.extend(defaults, ret);
}
/**
* @param {Record<string, any>} opts
*/
stacked100(opts) {
opts.dataLabels = opts.dataLabels || {};
opts.dataLabels.formatter = opts.dataLabels.formatter || void 0;
const existingDataLabelFormatter = opts.dataLabels.formatter;
opts.yaxis.forEach((yaxe, index) => {
opts.yaxis[index].min = 0;
opts.yaxis[index].max = 100;
});
const isBar = opts.chart.type === "bar";
if (isBar) {
opts.dataLabels.formatter = existingDataLabelFormatter || /**
* @param {any} val
*/
function(val) {
if (typeof val === "number") {
return val ? val.toFixed(0) + "%" : val;
}
return val;
};
}
return opts;
}
stackedBars() {
const barDefaults = this.bar();
return __spreadProps(__spreadValues({}, barDefaults), {
plotOptions: __spreadProps(__spreadValues({}, barDefaults.plotOptions), {
bar: __spreadProps(__spreadValues({}, barDefaults.plotOptions.bar), {
borderRadiusApplication: "end",
borderRadiusWhenStacked: "last"
})
})
});
}
// This function removes the left and right spacing in chart for line/area/scatter if xaxis type = category for those charts by converting xaxis = numeric. Numeric/Datetime xaxis prevents the unnecessary spacing in the left/right of the chart area
/**
* @param {Record<string, any>} opts
*/
convertCatToNumeric(opts) {
opts.xaxis.convertedCatToNumeric = true;
return opts;
}
/**
* @param {Record<string, any>} opts
* @param {any} cats
*/
convertCatToNumericXaxis(opts, cats) {
opts.xaxis.type = "numeric";
opts.xaxis.labels = opts.xaxis.labels || {};
opts.xaxis.labels.formatter = opts.xaxis.labels.formatter || /**
* @param {any} val
*/
function(val) {
return Utils$1.isNumber(val) ? Math.floor(val) : val;
};
const defaultFormatter = opts.xaxis.labels.formatter;
let labels = opts.xaxis.categories && opts.xaxis.categories.length ? opts.xaxis.categories : opts.labels;
if (cats && cats.length) {
labels = cats.map((c) => {
return Array.isArray(c) ? c : String(c);
});
}
if (labels && labels.length) {
opts.xaxis.labels.formatter = function(val) {
return Utils$1.isNumber(val) ? defaultFormatter(labels[Math.floor(val) - 1]) : defaultFormatter(val);
};
}
opts.xaxis.categories = [];
opts.labels = [];
opts.xaxis.tickAmount = opts.xaxis.tickAmount || "dataPoints";
return opts;
}
bubble() {
return {
dataLabels: {
style: {
colors: ["#fff"]
}
},
tooltip: {
shared: false,
intersect: true
},
xaxis: {
crosshairs: {
width: 0
}
},
fill: {
type: "solid",
gradient: {
shade: "light",
inverse: true,
shadeIntensity: 0.55,
opacityFrom: 0.4,
opacityTo: 0.8
}
}
};
}
scatter() {
return {
dataLabels: {
enabled: false
},
tooltip: {
shared: false,
intersect: true
},
markers: {
size: 6,
strokeWidth: 1,
hover: {
sizeOffset: 2
}
}
};
}
heatmap() {
return {
chart: {
stacked: false
},
fill: {
opacity: 1
},
dataLabels: {
style: {
colors: ["#fff"]
}
},
stroke: {
colors: ["#fff"]
},
tooltip: {
followCursor: true,
marker: {
show: false
},
x: {
show: false
}
},
legend: {
position: "top",
markers: {
shape: "square"
}
},
grid: {
padding: {
right: 20
}
}
};
}
treemap() {
return {
chart: {
zoom: {
enabled: false
}
},
dataLabels: {
style: {
fontSize: 14,
fontWeight: 600,
colors: ["#fff"]
}
},
stroke: {
show: true,
width: 2,
colors: ["#fff"]
},
legend: {
show: false
},
fill: {
opacity: 1,
gradient: {
stops: [0, 100]
}
},
tooltip: {
followCursor: true,
x: {
show: false
}
},
grid: {
padding: {
left: 0,
right: 0
}
},
xaxis: {
crosshairs: {
show: false
},
tooltip: {
enabled: false
}
}
};
}
pie() {
return {
chart: {
toolbar: {
show: false
}
},
plotOptions: {
pie: {
donut: {
labels: {
show: false
}
}
}
},
dataLabels: {
/**
* @param {number} val
*/
formatter(val) {
return val.toFixed(1) + "%";
},
style: {
colors: ["#fff"]
},
background: {
enabled: false
},
dropShadow: {
enabled: true
}
},
stroke: {
colors: ["#fff"]
},
fill: {
opacity: 1,
gradient: {
shade: "light",
stops: [0, 100]
}
},
tooltip: {
theme: "dark",
fillSeriesColor: true
},
legend: {
position: "right"
},
grid: {
padding: {
left: 0,
right: 0,
top: 0,
bottom: 0
}
}
};
}
donut() {
return {
chart: {
toolbar: {
show: false
}
},
dataLabels: {
/**
* @param {number} val
*/
formatter(val) {
return val.toFixed(1) + "%";
},
style: {
colors: ["#fff"]
},
background: {
enabled: false
},
dropShadow: {
enabled: true
}
},
stroke: {
colors: ["#fff"]
},
fill: {
opacity: 1,
gradient: {
shade: "light",
shadeIntensity: 0.35,
stops: [80, 100],
opacityFrom: 1,
opacityTo: 1
}
},
tooltip: {
theme: "dark",
fillSeriesColor: true
},
legend: {
position: "right"
},
grid: {
padding: {
left: 0,
right: 0,
top: 0,
bottom: 0
}
}
};
}
polarArea() {
return {
chart: {
toolbar: {
show: false
}
},
dataLabels: {
/**
* @param {number} val
*/
formatter(val) {
return val.toFixed(1) + "%";
},
enabled: false
},
stroke: {
show: true,
width: 2
},
fill: {
opacity: 0.7
},
tooltip: {
theme: "dark",
fillSeriesColor: true
},
legend: {
position: "right"
},
grid: {
padding: {
left: 0,
right: 0,
top: 0,
bottom: 0
}
}
};
}
radar() {
this.opts.yaxis[0].labels.offsetY = this.opts.yaxis[0].labels.offsetY ? this.opts.yaxis[0].labels.offsetY : 6;
return {
dataLabels: {
enabled: false,
style: {
fontSize: "11px"
}
},
stroke: {
width: 2
},
markers: {
size: 5,
strokeWidth: 1,
strokeOpacity: 1
},
fill: {
opacity: 0.2
},
tooltip: {
shared: false,
intersect: true,
followCursor: true
},
grid: {
show: false,
padding: {
left: 0,
right: 0,
top: 0,
bottom: 0
}
},
xaxis: {
labels: {
formatter: (val) => val,
style: {
colors: ["#a8a8a8"],
fontSize: "11px"
}
},
tooltip: {
enabled: false
},
crosshairs: {
show: false
}
}
};
}
radialBar() {
return {
chart: {
animations: {
dynamicAnimation: {
enabled: true,
speed: 800
}
},
toolbar: {
show: false
}
},
fill: {
gradient: {
shade: "dark",
shadeIntensity: 0.4,
inverseColors: false,
type: "diagonal2",
opacityFrom: 1,
opacityTo: 1,
stops: [70, 98, 100]
}
},
legend: {
show: false,
position: "right"
},
tooltip: {
enabled: false,
fillSeriesColor: true
},
grid: {
padding: {
left: 0,
right: 0,
top: 0,
bottom: 0
}
}
};
}
/**
* @param {import('../../types/internal').ChartStateW} w
* @param {number} seriesIndex
* @param {number} dataPointIndex
* @param {any[]} labels
* @param {string} chartType
*/
_getBoxTooltip(w, seriesIndex, dataPointIndex, labels, chartType) {
const o = w.candleData.seriesCandleO[seriesIndex][dataPointIndex];
const h = w.candleData.seriesCandleH[seriesIndex][dataPointIndex];
const m = w.candleData.seriesCandleM[seriesIndex][dataPointIndex];
const l = w.candleData.seriesCandleL[seriesIndex][dataPointIndex];
const c = w.candleData.seriesCandleC[seriesIndex][dataPointIndex];
const _si = (
/** @type {Record<string,any>} */
w.config.series[seriesIndex]
);
if (_si.type && _si.type !== chartType) {
return `<div class="apexcharts-custom-tooltip">
${_si.name ? _si.name : "series-" + (seriesIndex + 1)}: <strong>${w.seriesData.series[seriesIndex][dataPointIndex]}</strong>
</div>`;
} else {
return `<div class="apexcharts-tooltip-box apexcharts-tooltip-${w.config.chart.type}"><div>${labels[0]}: <span class="value">` + o + `</span></div><div>${labels[1]}: <span class="value">` + h + "</span></div>" + (m ? `<div>${labels[2]}: <span class="value">` + m + "</span></div>" : "") + `<div>${labels[3]}: <span class="value">` + l + `</span></div><div>${labels[4]}: <span class="value">` + c + "</span></div></div>";
}
}
}
const name = "en";
const options = { "months": ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], "shortMonths": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], "days": ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], "shortDays": ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], "toolbar": { "exportToSVG": "Download SVG", "exportToPNG": "Download PNG", "exportToCSV": "Download CSV", "menu": "Menu", "selection": "Selection", "selectionZoom": "Selection Zoom", "zoomIn": "Zoom In", "zoomOut": "Zoom Out", "pan": "Panning", "reset": "Reset Zoom" } };
const en = {
name,
options
};
class Options {
constructor() {
this.yAxis = {
show: true,
showAlways: false,
showForNullSeries: true,
seriesName: void 0,
opposite: false,
reversed: false,
logarithmic: false,
logBase: 10,
tickAmount: void 0,
stepSize: void 0,
forceNiceScale: false,
max: void 0,
min: void 0,
floating: false,
decimalsInFloat: void 0,
labels: {
show: true,
showDuplicates: false,
minWidth: 0,
maxWidth: 160,
offsetX: 0,
offsetY: 0,
align: void 0,
rotate: 0,
padding: 20,
style: {
colors: [],
fontSize: "11px",
fontWeight: 400,
fontFamily: void 0,
cssClass: ""
},
formatter: void 0
},
axisBorder: {
show: false,
color: "#e0e0e0",
width: 1,
offsetX: 0,
offsetY: 0
},
axisTicks: {
show: false,
color: "#e0e0e0",
width: 6,
offsetX: 0,
offsetY: 0
},
title: {
text: void 0,
rotate: -90,
offsetY: 0,
offsetX: 0,
style: {
color: void 0,
fontSize: "11px",
fontWeight: 900,
fontFamily: void 0,
cssClass: ""
}
},
tooltip: {
enabled: false,
offsetX: 0
},
crosshairs: {
show: true,
position: "front",
stroke: {
color: "#b6b6b6",
width: 1,
dashArray: 0
}
}
};
this.pointAnnotation = {
id: void 0,
x: 0,
y: null,
yAxisIndex: 0,
seriesIndex: void 0,
mouseEnter: void 0,
mouseLeave: void 0,
click: void 0,
marker: {
size: 4,
fillColor: "#fff",
strokeWidth: 2,
strokeColor: "#333",
shape: "circle",
offsetX: 0,
offsetY: 0,
// radius: 2, // DEPRECATED
cssClass: ""
},
label: {
borderColor: "#c2c2c2",
borderWidth: 1,
borderRadius: 2,
text: void 0,
textAnchor: "middle",
offsetX: 0,
offsetY: 0,
mouseEnter: void 0,
mouseLeave: void 0,
click: void 0,
style: {
background: "#fff",
color: void 0,
fontSize: "11px",
fontFamily: void 0,
fontWeight: 400,
cssClass: "",
padding: {
left: 5,
right: 5,
top: 2,
bottom: 2
}
}
},
customSVG: {
// this will be deprecated in the next major version as it is going to be replaced with a better alternative below (image)
SVG: void 0,
cssClass: void 0,
offsetX: 0,
offsetY: 0
},
image: {
path: void 0,
width: 20,
height: 20,
offsetX: 0,
offsetY: 0
}
};
this.yAxisAnnotation = {
id: void 0,
y: 0,
y2: null,
strokeDashArray: 1,
fillColor: "#c2c2c2",
borderColor: "#c2c2c2",
borderWidth: 1,
opacity: 0.3,
offsetX: 0,
offsetY: 0,
width: "100%",
yAxisIndex: 0,
label: {
borderColor: "#c2c2c2",
borderWidth: 1,
borderRadius: 2,
text: void 0,
textAnchor: "end",
position: "right",
offsetX: 0,
offsetY: -3,
mouseEnter: void 0,
mouseLeave: void 0,
click: void 0,
style: {
background: "#fff",
color: void 0,
fontSize: "11px",
fontFamily: void 0,
fontWeight: 400,
cssClass: "",
padding: {
left: 5,
right: 5,
top: 2,
bottom: 2
}
}
}
};
this.xAxisAnnotation = {
id: void 0,
x: 0,
x2: null,
strokeDashArray: 1,
fillColor: "#c2c2c2",
borderColor: "#c2c2c2",
borderWidth: 1,
opacity: 0.3,
offsetX: 0,
offsetY: 0,
label: {
borderColor: "#c2c2c2",
borderWidth: 1,
borderRadius: 2,
text: void 0,
textAnchor: "middle",
orientation: "vertical",
position: "top",
offsetX: 0,
offsetY: 0,
mouseEnter: void 0,
mouseLeave: void 0,
click: void 0,
style: {
background: "#fff",
color: void 0,
fontSize: "11px",
fontFamily: void 0,
fontWeight: 400,
cssClass: "",
padding: {
left: 5,
right: 5,
top: 2,
bottom: 2
}
}
}
};
this.text = {
x: 0,
y: 0,
text: "",
textAnchor: "start",
foreColor: void 0,
fontSize: "13px",
fontFamily: void 0,
fontWeight: 400,
appendTo: ".apexcharts-annotations",
backgroundColor: "transparent",
borderColor: "#c2c2c2",
borderRadius: 0,
borderWidth: 0,
paddingLeft: 4,
paddingRight: 4,
paddingTop: 2,
paddingBottom: 2
};
}
init() {
return {
annotations: {
yaxis: [this.yAxisAnnotation],
xaxis: [this.xAxisAnnotation],
points: [this.pointAnnotation],
texts: [],
images: [],
shapes: []
},
chart: {
animations: {
enabled: true,
speed: 800,
animateGradually: {
delay: 150,
enabled: true
},
dynamicAnimation: {
enabled: true,
speed: 350
}
},
background: "",
locales: [en],
defaultLocale: "en",
dropShadow: {
enabled: false,
enabledOnSeries: void 0,
top: 2,
left: 2,
blur: 4,
color: "#000",
opacity: 0.7
},
events: {
animationEnd: void 0,
beforeMount: void 0,
mounted: void 0,
updated: void 0,
click: void 0,
mouseMove: void 0,
mouseLeave: void 0,
xAxisLabelClick: void 0,
legendClick: void 0,
markerClick: void 0,
selection: void 0,
dataPointSelection: void 0,
dataPointMouseEnter: void 0,
dataPointMouseLeave: void 0,
beforeZoom: void 0,
beforeResetZoom: void 0,
zoomed: void 0,
scrolled: void 0,
brushScrolled: void 0,
keyDown: void 0,
keyUp: void 0
},
foreColor: "#373d3f",
fontFamily: "Helvetica, Arial, sans-serif",
height: "auto",
parentHeightOffset: 15,
redrawOnParentResize: true,
redrawOnWindowResize: true,
id: void 0,
group: void 0,
nonce: void 0,
offsetX: 0,
offsetY: 0,
injectStyleSheet: true,
selection: {
enabled: false,
type: "x",
// selectedPoints: undefined, // default datapoints that should be selected automatically
fill: {
color: "#24292e",
opacity: 0.1
},
stroke: {
width: 1,
color: "#24292e",
opacity: 0.4,
dashArray: 3
},
xaxis: {
min: void 0,
max: void 0
},
yaxis: {
min: void 0,
max: void 0
}
},
sparkline: {
enabled: false
},
brush: {
enabled: false,
autoScaleYaxis: true,
target: void 0,
targets: void 0
},
stacked: false,
stackOnlyBar: true,
// mixed chart with stacked bars and line series - incorrect line draw #907
stackType: "normal",
toolbar: {
show: true,
offsetX: 0,
offsetY: 0,
tools: {
download: true,
selection: true,
zoom: true,
zoomin: true,
zoomout: true,
pan: true,
reset: true,
customIcons: []
},
export: {
csv: {
filename: void 0,
columnDelimiter: ",",
headerCategory: "category",
headerValue: "value",
categoryFormatter: void 0,
valueFormatter: void 0
},
png: {
filename: void 0
},
svg: {
filename: void 0
},
scale: void 0,
width: void 0
},
autoSelected: "zoom"
// accepts -> zoom, pan, selection
},
type: "line",
width: "100%",
zoom: {
enabled: true,
type: "x",
autoScaleYaxis: false,
allowMouseWheelZoom: true,
zoomedArea: {
fill: {
color: "#90CAF9",
opacity: 0.4
},
stroke: {
color: "#0D47A1",
opacity: 0.4,
width: 1
}
}
},
accessibility: {
enabled: true,
description: void 0,
announcements: {
enabled: true
},
keyboard: {
enabled: true,
navigation: {
enabled: true,
wrapAround: false
}
}
},
dataReducer: {
enabled: false,
algorithm: "lttb",
targetPoints: 250,
threshold: 500
}
},
parsing: {
x: void 0,
y: void 0
},
plotOptions: {
line: {
isSlopeChart: false,
colors: {
threshold: 0,
colorAboveThreshold: void 0,
colorBelowThreshold: void 0
}
},
area: {
fillTo: "origin"
},
bar: {
horizontal: false,
columnWidth: "70%",
// should be in percent 0 - 100
barHeight: "70%",
// should be in percent 0 - 100
distributed: false,
borderRadius: 0,
borderRadiusApplication: "around",
// [around, end]
borderRadiusWhenStacked: "last",
// [all, last]
rangeBarOverlap: true,
rangeBarGroupRows: false,
hideZeroBarsWhenGrouped: false,
isDumbbell: false,
dumbbellColors: void 0,
isFunnel: false,
isFunnel3d: true,
colors: {
ranges: [],
backgroundBarColors: [],
backgroundBarOpacity: 1,
backgroundBarRadius: 0
},
dataLabels: {
position: "top",
// top, center, bottom
maxItems: 100,
hideOverflowingLabels: true,
orientation: "horizontal",
total: {
enabled: false,
formatter: void 0,
offsetX: 0,
offsetY: 0,
style: {
color: "#373d3f",
fontSize: "12px",
fontFamily: void 0,
fontWeight: 600
}
}
}
},
bubble: {
zScaling: true,
minBubbleRadius: void 0,
maxBubbleRadius: void 0
},
candlestick: {
colors: {
upward: "#00B746",
downward: "#EF403C"
},
wick: {
useFillColor: true
}
},
boxPlot: {
colors: {
upper: "#00E396",
lower: "#008FFB"
}
},
heatmap: {
radius: 2,
enableShades: true,
shadeIntensity: 0.5,
reverseNegativeShade: false,
distributed: false,
useFillColorAsStroke: false,
colorScale: {
inverse: false,
ranges: [],
min: void 0,
max: void 0
}
},
treemap: {
enableShades: true,
shadeIntensity: 0.5,
distributed: false,
reverseNegativeShade: false,
useFillColorAsStroke: false,
borderRadius: 4,
dataLabels: {
format: "scale"
// scale | truncate
},
colorScale: {
inverse: false,
ranges: [],
min: void 0,
max: void 0
},
seriesTitle: {
show: true,
offsetY: 1,
offsetX: 1,
borderColor: "#000",
borderWidth: 1,
borderRadius: 2,
style: {
background: "rgba(0, 0, 0, 0.6)",
color: "#fff",
fontSize: "12px",
fontFamily: void 0,
fontWeight: 400,
cssClass: "",
padding: {
left: 6,
right: 6,
top: 2,
bottom: 2
}
}
}
},
radialBar: {
inverseOrder: false,
startAngle: 0,
endAngle: 360,
offsetX: 0,
offsetY: 0,
hollow: {
margin: 5,
size: "50%",
background: "transparent",
image: void 0,
imageWidth: 150,
imageHeight: 150,
imageOffsetX: 0,
imageOffsetY: 0,
imageClipped: true,
position: "front",
dropShadow: {
enabled: false,
top: 0,
left: 0,
blur: 3,
color: "#000",
opacity: 0.5
}
},
track: {
show: true,
startAngle: void 0,
endAngle: void 0,
background: "#f2f2f2",
strokeWidth: "97%",
opacity: 1,
margin: 5,
// margin is in pixels
dropShadow: {
enabled: false,
top: 0,
left: 0,
blur: 3,
color: "#000",
opacity: 0.5
}
},
dataLabels: {
show: true,
name: {
show: true,
fontSize: "16px",
fontFamily: void 0,
fontWeight: 600,
color: void 0,
offsetY: 0,
/**
* @param {any} val
*/
formatter(val) {
return val;
}
},
value: {
show: true,
fontSize: "14px",
fontFamily: void 0,
fontWeight: 400,
color: void 0,
offsetY: 16,
/**
* @param {any} val
*/
formatter(val) {
return val + "%";
}
},
total: {
show: false,
label: "Total",
fontSize: "16px",
fontWeight: 600,
fontFamily: void 0,
color: void 0,
/**
* @param {import('../../types/internal').ChartStateW} w
*/
formatter(w) {
return (
/**
* @param {number} a
* @param {number} b
*/
w.globals.seriesTotals.reduce((a, b) => a + b, 0) / w.seriesData.series.length + "%"
);
}
}
},
barLabels: {
enabled: false,
offsetX: 0,
offsetY: 0,
useSeriesColors: true,
fontFamily: void 0,
fontWeight: 600,
fontSize: "16px",
/**
* @param {any} val
*/
formatter(val) {
return val;
},
onClick: void 0
}
},
pie: {
customScale: 1,
offsetX: 0,
offsetY: 0,
startAngle: 0,
endAngle: 360,
expandOnClick: true,
dataLabels: {
// These are the percentage values which are displayed on slice
offset: 0,
// offset by which labels will move outside
minAngleToShowLabel: 10
},
donut: {
size: "65%",
background: "transparent",
labels: {
// These are the inner labels appearing inside donut
show: false,
name: {
show: true,
fontSize: "16px",
fontFamily: void 0,
fontWeight: 600,
color: void 0,
offsetY: -10,
/**
* @param {any} val
*/
formatter(val) {
return val;
}
},
value: {
show: true,
fontSize: "20px",
fontFamily: void 0,
fontWeight: 400,
color: void 0,
offsetY: 10,
/**
* @param {any} val
*/
formatter(val) {
return val;
}
},
total: {
show: false,
showAlways: false,
label: "Total",
fontSize: "16px",
fontWeight: 400,
fontFamily: void 0,
color: void 0,
/**
* @param {import('../../types/internal').ChartStateW} w
*/
formatter(w) {
return w.globals.seriesTotals.reduce((a, b) => a + b, 0);
}
}
}
}
},
polarArea: {
rings: {
strokeWidth: 1,
strokeColor: "#e8e8e8"
},
spokes: {
strokeWidth: 1,
connectorColors: "#e8e8e8"
}
},
radar: {
size: void 0,
offsetX: 0,
offsetY: 0,
polygons: {
// strokeColor: '#e8e8e8', // should be deprecated in the minor version i.e 3.2
strokeWidth: 1,
strokeColors: "#e8e8e8",
connectorColors: "#e8e8e8",
fill: {
colors: void 0
}
}
}
},
colors: void 0,
dataLabels: {
enabled: true,
enabledOnSeries: void 0,
/**
* @param {any} val
*/
formatter(val) {
return val !== null ? val : "";
},
textAnchor: "middle",
distributed: false,
offsetX: 0,
offsetY: 0,
style: {
fontSize: "12px",
fontFamily: void 0,
fontWeight: 600,
colors: void 0
},
background: {
enabled: true,
foreColor: "#fff",
backgroundColor: void 0,
borderRadius: 2,
padding: 4,
opacity: 0.9,
borderWidth: 1,
borderColor: "#fff",
dropShadow: {
enabled: false,
top: 1,
left: 1,
blur: 1,
color: "#000",
opacity: 0.8
}
},
dropShadow: {
enabled: false,
top: 1,
left: 1,
blur: 1,
color: "#000",
opacity: 0.8
}
},
fill: {
type: "solid",
colors: void 0,
// array of colors
opacity: 0.85,
gradient: {
shade: "dark",
type: "horizontal",
shadeIntensity: 0.5,
gradientToColors: void 0,
inverseColors: true,
opacityFrom: 1,
opacityTo: 1,
stops: [0, 50, 100],
colorStops: []
},
image: {
src: [],
width: void 0,
// optional
height: void 0
// optional
},
pattern: {
style: "squares",
// String | Array of Strings
width: 6,
height: 6,
strokeWidth: 2
}
},
forecastDataPoints: {
count: 0,
fillOpacity: 0.5,
strokeWidth: void 0,
dashArray: 4
},
grid: {
show: true,
borderColor: "#e0e0e0",
strokeDashArray: 0,
position: "back",
xaxis: {
lines: {
show: false
}
},
yaxis: {
lines: {
show: true
}
},
row: {
colors: void 0,
// takes as array which will be repeated on rows
opacity: 0.5
},
column: {
colors: void 0,
// takes an array which will be repeated on columns
opacity: 0.5
},
padding: {
top: 0,
right: 10,
bottom: 0,
left: 12
}
},
labels: [],
legend: {
show: true,
showForSingleSeries: false,
showForNullSeries: true,
showForZeroSeries: true,
floating: false,
position: "bottom",
// whether to position legends in 1 of 4
// direction - top, bottom, left, right
horizontalAlign: "center",
// when position top/bottom, you can specify whether to align legends left, right or center
inverseOrder: false,
fontSize: "12px",
fontFamily: void 0,
fontWeight: 400,
width: void 0,
height: void 0,
formatter: void 0,
tooltipHoverFormatter: void 0,
offsetX: -20,
offsetY: 4,
customLegendItems: [],
clusterGroupedSeries: true,
clusterGroupedSeriesOrientation: "vertical",
labels: {
colors: void 0,
useSeriesColors: false
},
markers: {
size: 7,
fillColors: void 0,
strokeWidth: 1,
shape: void 0,
offsetX: 0,
offsetY: 0,
customHTML: void 0,
onClick: void 0
},
itemMargin: {
horizontal: 5,
vertical: 4
},
onItemClick: {
toggleDataSeries: true
},
onItemHover: {
highlightDataSeries: true
}
},
markers: {
discrete: [],
size: 0,
colors: void 0,
strokeColors: "#fff",
strokeWidth: 2,
strokeOpacity: 0.9,
strokeDashArray: 0,
fillOpacity: 1,
shape: "circle",
offsetX: 0,
offsetY: 0,
showNullDataPoints: true,
onClick: void 0,
onDblClick: void 0,
hover: {
size: void 0,
sizeOffset: 3
}
},
noData: {
text: void 0,
align: "center",
offsetX: 0,
offsetY: 0,
style: {
color: void 0,
fontSize: "14px",
fontFamily: void 0
}
},
responsive: [],
// breakpoints should follow ascending order 400, then 700, then 1000
series: void 0,
states: {
hover: {
filter: {
type: "lighten"
}
},
active: {
allowMultipleDataPointsSelection: false,
filter: {
type: "darken"
}
}
},
title: {
text: void 0,
align: "left",
margin: 5,
offsetX: 0,
offsetY: 0,
floating: false,
style: {
fontSize: "14px",
fontWeight: 900,
fontFamily: void 0,
color: void 0
}
},
subtitle: {
text: void 0,
align: "left",
margin: 5,
offsetX: 0,
offsetY: 30,
floating: false,
style: {
fontSize: "12px",
fontWeight: 400,
fontFamily: void 0,
color: void 0
}
},
stroke: {
show: true,
curve: "smooth",
// "smooth" / "straight" / "monotoneCubic" / "stepline" / "linestep"
lineCap: "butt",
// round, butt , square
width: 2,
colors: void 0,
// array of colors
dashArray: 0,
// single value or array of values
fill: {
type: "solid",
colors: void 0,
// array of colors
opacity: 0.85,
gradient: {
shade: "dark",
type: "horizontal",
shadeIntensity: 0.5,
gradientToColors: void 0,
inverseColors: true,
opacityFrom: 1,
opacityTo: 1,
stops: [0, 50, 100],
colorStops: []
}
}
},
tooltip: {
enabled: true,
enabledOnSeries: void 0,
shared: true,
hideEmptySeries: false,
followCursor: false,
// when disabled, the tooltip will show on top of the series instead of mouse position
intersect: false,
// when enabled, tooltip will only show when user directly hovers over point
inverseOrder: false,
custom: void 0,
fillSeriesColor: false,
theme: "light",
cssClass: "",
style: {
fontSize: "12px",
fontFamily: void 0
},
onDatasetHover: {
highlightDataSeries: false
},
x: {
// x value
show: true,
format: "dd MMM",
// dd/MM, dd MMM yy, dd MMM yyyy
formatter: void 0
// a custom user supplied formatter function
},
y: {
formatter: void 0,
title: {
/**
* @param {string} seriesName
*/
formatter(seriesName) {
return seriesName ? seriesName + ": " : "";
}
}
},
z: {
formatter: void 0,
title: "Size: "
},
marker: {
show: true,
fillColors: void 0
},
items: {
display: "flex"
},
fixed: {
enabled: false,
position: "topRight",
// topRight, topLeft, bottomRight, bottomLeft
offsetX: 0,
offsetY: 0
}
},
xaxis: {
type: "category",
categories: [],
convertedCatToNumeric: false,
// internal property which should not be altered outside
offsetX: 0,
offsetY: 0,
overwriteCategories: void 0,
labels: {
show: true,
rotate: -45,
rotateAlways: false,
hideOverlappingLabels: true,
trim: false,
minHeight: void 0,
maxHeight: 120,
showDuplicates: true,
style: {
colors: [],
fontSize: "12px",
fontWeight: 400,
fontFamily: void 0,
cssClass: ""
},
offsetX: 0,
offsetY: 0,
format: void 0,
formatter: void 0,
// custom formatter function which will override format
datetimeUTC: true,
datetimeFormatter: {
year: "yyyy",
month: "MMM 'yy",
day: "dd MMM",
hour: "HH:mm",
minute: "HH:mm:ss",
second: "HH:mm:ss"
}
},
group: {
groups: [],
style: {
colors: [],
fontSize: "12px",
fontWeight: 400,
fontFamily: void 0,
cssClass: ""
}
},
axisBorder: {
show: true,
color: "#e0e0e0",
width: "100%",
height: 1,
offsetX: 0,
offsetY: 0
},
axisTicks: {
show: true,
color: "#e0e0e0",
height: 6,
offsetX: 0,
offsetY: 0
},
stepSize: void 0,
tickAmount: void 0,
tickPlacement: "on",
min: void 0,
max: void 0,
range: void 0,
floating: false,
decimalsInFloat: void 0,
position: "bottom",
title: {
text: void 0,
offsetX: 0,
offsetY: 0,
style: {
color: void 0,
fontSize: "12px",
fontWeight: 900,
fontFamily: void 0,
cssClass: ""
}
},
crosshairs: {
show: true,
width: 1,
// tickWidth/barWidth or an integer
position: "back",
opacity: 0.9,
stroke: {
color: "#b6b6b6",
width: 1,
dashArray: 3
},
fill: {
type: "solid",
// solid, gradient
color: "#B1B9C4",
gradient: {
colorFrom: "#D8E3F0",
colorTo: "#BED1E6",
stops: [0, 100],
opacityFrom: 0.4,
opacityTo: 0.5
}
},
dropShadow: {
enabled: false,
left: 0,
top: 0,
blur: 1,
opacity: 0.8
}
},
tooltip: {
enabled: true,
offsetY: 0,
formatter: void 0,
style: {
fontSize: "12px",
fontFamily: void 0
}
}
},
yaxis: this.yAxis,
theme: {
mode: "",
palette: "palette1",
// If defined, it will overwrite globals.colors variable
monochrome: {
// monochrome allows you to select just 1 color and fill out the rest with light/dark shade (intensity can be selected)
enabled: false,
color: "#008FFB",
shadeTo: "light",
shadeIntensity: 0.65
},
accessibility: {
colorBlindMode: ""
// '' | 'deuteranopia' | 'protanopia' | 'tritanopia' | 'highContrast'
}
}
};
}
}
class Config {
/**
* @param {Record<string, any>} opts
*/
constructor(opts) {
this.opts = opts;
}
/** @param {{responsiveOverride: any}} opts */
init({ responsiveOverride }) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
let opts = this.opts;
const options2 = new Options();
const defaults = new Defaults(opts);
this.chartType = opts.chart.type;
opts = this.extendYAxis(opts);
opts = this.extendAnnotations(opts);
let config = options2.init();
let newDefaults = {};
if (opts && typeof opts === "object") {
let chartDefaults = {};
const chartTypes = [
"line",
"area",
"bar",
"candlestick",
"boxPlot",
"rangeBar",
"rangeArea",
"bubble",
"scatter",
"heatmap",
"treemap",
"pie",
"polarArea",
"donut",
"radar",
"radialBar"
];
if (chartTypes.indexOf(opts.chart.type) !== -1) {
chartDefaults = /** @type {any} */
defaults[opts.chart.type]();
} else {
chartDefaults = defaults.line();
}
if ((_b = (_a = opts.plotOptions) == null ? void 0 : _a.bar) == null ? void 0 : _b.isFunnel) {
chartDefaults = defaults.funnel();
}
if (opts.chart.stacked && opts.chart.type === "bar") {
chartDefaults = defaults.stackedBars();
}
if ((_c = opts.chart.brush) == null ? void 0 : _c.enabled) {
chartDefaults = defaults.brush(chartDefaults);
}
if ((_e = (_d = opts.plotOptions) == null ? void 0 : _d.line) == null ? void 0 : _e.isSlopeChart) {
chartDefaults = defaults.slope();
}
if (opts.chart.stacked && opts.chart.stackType === "100%") {
opts = defaults.stacked100(opts);
}
if ((_g = (_f = opts.plotOptions) == null ? void 0 : _f.bar) == null ? void 0 : _g.isDumbbell) {
opts = defaults.dumbbell(opts);
}
this.checkForDarkTheme(Environment.getApex());
this.checkForDarkTheme(opts);
opts.xaxis = opts.xaxis || Environment.getApex().xaxis || {};
if (!responsiveOverride) {
opts.xaxis.convertedCatToNumeric = false;
}
opts = this.checkForCatToNumericXAxis(this.chartType, chartDefaults, opts);
if (((_h = opts.chart.sparkline) == null ? void 0 : _h.enabled) || ((_j = (_i = Environment.getApex().chart) == null ? void 0 : _i.sparkline) == null ? void 0 : _j.enabled)) {
chartDefaults = defaults.sparkline(chartDefaults);
}
newDefaults = Utils$1.extend(config, chartDefaults);
}
const mergedWithDefaultConfig = Utils$1.extend(
newDefaults,
Environment.getApex()
);
config = Utils$1.extend(mergedWithDefaultConfig, opts);
config = this.handleUserInputErrors(config);
return config;
}
/**
* @param {string} chartType
* @param {Record<string, any>} chartDefaults
* @param {Record<string, any>} opts
*/
checkForCatToNumericXAxis(chartType, chartDefaults, opts) {
var _a, _b;
const defaults = new Defaults(opts);
const isBarHorizontal = (chartType === "bar" || chartType === "boxPlot") && ((_b = (_a = opts.plotOptions) == null ? void 0 : _a.bar) == null ? void 0 : _b.horizontal);
const unsupportedZoom = chartType === "pie" || chartType === "polarArea" || chartType === "donut" || chartType === "radar" || chartType === "radialBar" || chartType === "heatmap";
const notNumericXAxis = opts.xaxis.type !== "datetime" && opts.xaxis.type !== "numeric";
const tickPlacement = opts.xaxis.tickPlacement ? opts.xaxis.tickPlacement : chartDefaults.xaxis && chartDefaults.xaxis.tickPlacement;
if (!isBarHorizontal && !unsupportedZoom && notNumericXAxis && tickPlacement !== "between") {
opts = defaults.convertCatToNumeric(opts);
}
return opts;
}
/**
* @param {Record<string, any>} opts
* @param {import('../../types/internal').ChartStateW} [w]
*/
extendYAxis(opts, w) {
const options2 = new Options();
if (typeof opts.yaxis === "undefined" || !opts.yaxis || Array.isArray(opts.yaxis) && opts.yaxis.length === 0) {
opts.yaxis = {};
}
const globalApex = Environment.getApex();
if (opts.yaxis.constructor !== Array && globalApex.yaxis && globalApex.yaxis.constructor !== Array) {
opts.yaxis = Utils$1.extend(opts.yaxis, globalApex.yaxis);
}
if (opts.yaxis.constructor !== Array) {
opts.yaxis = [Utils$1.extend(options2.yAxis, opts.yaxis)];
} else {
opts.yaxis = Utils$1.extendArray(opts.yaxis, options2.yAxis);
}
let isLogY = false;
opts.yaxis.forEach((y) => {
if (y.logarithmic) {
isLogY = true;
}
});
let series = opts.series;
if (w && !series) {
series = w.config.series;
}
if (isLogY && series.length !== opts.yaxis.length && series.length) {
opts.yaxis = series.map((s, i) => {
if (!s.name) {
series[i].name = `series-${i + 1}`;
}
if (opts.yaxis[i]) {
opts.yaxis[i].seriesName = series[i].name;
return opts.yaxis[i];
} else {
const newYaxis = Utils$1.extend(options2.yAxis, opts.yaxis[0]);
newYaxis.show = false;
return newYaxis;
}
});
}
if (isLogY && series.length > 1 && series.length !== opts.yaxis.length) {
console.warn(
"A multi-series logarithmic chart should have equal number of series and y-axes"
);
}
return opts;
}
// annotations also accepts array, so we need to extend them manually
/**
* @param {Record<string, any>} opts
*/
extendAnnotations(opts) {
if (typeof opts.annotations === "undefined") {
opts.annotations = {};
opts.annotations.yaxis = [];
opts.annotations.xaxis = [];
opts.annotations.points = [];
}
opts = this.extendYAxisAnnotations(opts);
opts = this.extendXAxisAnnotations(opts);
opts = this.extendPointAnnotations(opts);
return opts;
}
/**
* @param {Record<string, any>} opts
*/
extendYAxisAnnotations(opts) {
const options2 = new Options();
opts.annotations.yaxis = Utils$1.extendArray(
typeof opts.annotations.yaxis !== "undefined" ? opts.annotations.yaxis : [],
options2.yAxisAnnotation
);
return opts;
}
/**
* @param {Record<string, any>} opts
*/
extendXAxisAnnotations(opts) {
const options2 = new Options();
opts.annotations.xaxis = Utils$1.extendArray(
typeof opts.annotations.xaxis !== "undefined" ? opts.annotations.xaxis : [],
options2.xAxisAnnotation
);
return opts;
}
/**
* @param {Record<string, any>} opts
*/
extendPointAnnotations(opts) {
const options2 = new Options();
opts.annotations.points = Utils$1.extendArray(
typeof opts.annotations.points !== "undefined" ? opts.annotations.points : [],
options2.pointAnnotation
);
return opts;
}
/**
* @param {Record<string, any>} opts
*/
checkForDarkTheme(opts) {
if (opts.theme && opts.theme.mode === "dark") {
if (!opts.tooltip) {
opts.tooltip = {};
}
if (opts.tooltip.theme !== "light") {
opts.tooltip.theme = "dark";
}
if (!opts.chart.foreColor) {
opts.chart.foreColor = "#f6f7f8";
}
if (!opts.theme.palette) {
opts.theme.palette = "palette4";
}
}
}
/**
* @param {any} opts
*/
handleUserInputErrors(opts) {
const config = opts;
if (config.tooltip.shared && config.tooltip.intersect) {
throw new Error(
"tooltip.shared cannot be enabled when tooltip.intersect is true. Turn off any other option by setting it to false."
);
}
if (config.chart.type === "bar" && config.plotOptions.bar.horizontal) {
if (config.yaxis.length > 1) {
throw new Error(
"Multiple Y Axis for bars are not supported. Switch to column chart by setting plotOptions.bar.horizontal=false"
);
}
if (config.yaxis[0].reversed) {
config.yaxis[0].opposite = true;
}
config.xaxis.tooltip.enabled = false;
config.yaxis[0].tooltip.enabled = false;
config.chart.zoom.enabled = false;
}
if (config.chart.type === "bar" || config.chart.type === "rangeBar") {
if (config.tooltip.shared) {
if (config.xaxis.crosshairs.width === "barWidth" && config.series.length > 1) {
config.xaxis.crosshairs.width = "tickWidth";
}
}
}
if (config.chart.type === "candlestick" || config.chart.type === "boxPlot") {
if (config.yaxis[0].reversed) {
console.warn(
`Reversed y-axis in ${config.chart.type} chart is not supported.`
);
config.yaxis[0].reversed = false;
}
}
return config;
}
}
const LINE_HEIGHT_RATIO = 1.618;
const NICE_SCALE_ALLOWED_MAG_MSD = [
[1, 1, 2, 5, 5, 5, 10, 10, 10, 10, 10],
[1, 1, 2, 5, 5, 5, 10, 10, 10, 10, 10]
];
const NICE_SCALE_DEFAULT_TICKS = [
1,
2,
4,
4,
6,
6,
6,
6,
6,
6,
6,
6,
6,
6,
6,
6,
6,
6,
12,
12,
12,
12,
12,
12,
12,
12,
12,
24
];
class Globals {
/**
* @param {any} gl
*/
initGlobalVars(gl) {
gl.series = [];
gl.seriesCandleO = [];
gl.seriesCandleH = [];
gl.seriesCandleM = [];
gl.seriesCandleL = [];
gl.seriesCandleC = [];
gl.seriesRangeStart = [];
gl.seriesRangeEnd = [];
gl.seriesRange = [];
gl.seriesPercent = [];
gl.seriesGoals = [];
gl.seriesX = [];
gl.seriesZ = [];
gl.seriesNames = [];
gl.seriesTotals = [];
gl.seriesLog = [];
gl.seriesColors = [];
gl.stackedSeriesTotals = [];
gl.seriesXvalues = [];
gl.seriesYvalues = [];
gl.dataWasParsed = false;
gl.originalSeries = null;
gl.maxValsInArrayIndex = 0;
gl.yValueDecimal = 0;
gl.allSeriesHasEqualX = true;
gl.labels = [];
gl.hasXaxisGroups = false;
gl.groups = [];
gl.barGroups = [];
gl.lineGroups = [];
gl.areaGroups = [];
gl.hasSeriesGroups = false;
gl.seriesGroups = [];
gl.categoryLabels = [];
gl.timescaleLabels = [];
gl.noLabelsProvided = false;
gl.isXNumeric = false;
gl.skipLastTimelinelabel = false;
gl.skipFirstTimelinelabel = false;
gl.isDataXYZ = false;
gl.isMultiLineX = false;
gl.isMultipleYAxis = false;
gl.maxY = -Number.MAX_VALUE;
gl.minY = Number.MIN_VALUE;
gl.minYArr = [];
gl.maxYArr = [];
gl.maxX = -Number.MAX_VALUE;
gl.minX = Number.MAX_VALUE;
gl.initialMaxX = -Number.MAX_VALUE;
gl.initialMinX = Number.MAX_VALUE;
gl.maxDate = 0;
gl.minDate = Number.MAX_VALUE;
gl.minZ = Number.MAX_VALUE;
gl.maxZ = -Number.MAX_VALUE;
gl.minXDiff = Number.MAX_VALUE;
gl.yAxisScale = [];
gl.xAxisScale = null;
gl.xAxisTicksPositions = [];
gl.xRange = 0;
gl.yRange = [];
gl.zRange = 0;
gl.dataPoints = 0;
gl.xTickAmount = 0;
gl.multiAxisTickAmount = 0;
gl.disableZoomIn = false;
gl.disableZoomOut = false;
gl.yLabelsCoords = [];
gl.yTitleCoords = [];
gl.barPadForNumericAxis = 0;
gl.padHorizontal = 0;
gl.rotateXLabels = false;
gl.overlappingXLabels = false;
gl.radialSize = 0;
gl.barHeight = 0;
gl.barWidth = 0;
gl.animationEnded = false;
gl.resizeTimer = null;
gl.selectionResizeTimer = null;
gl.lastWheelExecution = 0;
gl.delayedElements = [];
gl.pointsArray = [];
gl.dataLabelsRects = [];
gl.lastDrawnDataLabelsIndexes = [];
gl.textRectsCache = /* @__PURE__ */ new Map();
gl.domCache = /* @__PURE__ */ new Map();
gl.dimensionCache = {};
gl.cachedSelectors = {};
if (!gl.seriesNS) {
this._attachNamespaces(gl);
}
}
/**
* Attach domain-grouped namespace sub-objects onto gl.
* Each sub-object is a plain object whose properties are defined as
* getters/setters that read/write the canonical flat properties on gl.
* This means there is exactly ONE storage location per value — no copies,
* no sync needed.
*
* Namespaces:
* gl.series — parsed series data and chart-type-specific arrays
* gl.axes — axis bounds, scales, ranges, tick state
* gl.layout — SVG/grid dimensions, translations, label sizes
* gl.cache — DOM caches, timers, observers, drawing scratch space
*
* Note: interact state lives on w.interact (not gl) — see Base.js.
* @param {any} gl
*/
_attachNamespaces(gl) {
const proxy = (ns, key, nsKey = key) => {
Object.defineProperty(ns, nsKey, {
get() {
return gl[key];
},
set(v) {
gl[key] = v;
},
enumerable: true,
configurable: true
});
};
const seriesNS = {};
proxy(seriesNS, "series", "data");
for (const key of [
"seriesNames",
"seriesX",
"seriesZ",
"seriesXvalues",
"seriesYvalues",
"seriesGoals",
"seriesLog",
"seriesColors",
"seriesPercent",
"seriesTotals",
"stackedSeriesTotals",
"seriesCandleO",
"seriesCandleH",
"seriesCandleM",
"seriesCandleL",
"seriesCandleC",
"seriesRangeStart",
"seriesRangeEnd",
"seriesRange",
"seriesYAxisMap",
"seriesYAxisReverseMap",
"seriesGroups",
"barGroups",
"lineGroups",
"areaGroups",
"originalSeries",
"collapsedSeries",
"collapsedSeriesIndices",
"ancillaryCollapsedSeries",
"ancillaryCollapsedSeriesIndices",
"allSeriesCollapsed",
"risingSeries",
"previousPaths",
"ignoreYAxisIndexes",
"labels",
"categoryLabels",
"timescaleLabels",
"groups"
]) {
proxy(seriesNS, key);
}
Object.defineProperty(gl, "seriesNS", {
value: seriesNS,
writable: false,
enumerable: false,
configurable: true
});
const axesNS = {};
for (const key of [
"minX",
"maxX",
"initialMinX",
"initialMaxX",
"minY",
"maxY",
"minYArr",
"maxYArr",
"minZ",
"maxZ",
"minDate",
"maxDate",
"minXDiff",
"xRange",
"yRange",
"zRange",
"xAxisScale",
"yAxisScale",
"xAxisTicksPositions",
"xTickAmount",
"multiAxisTickAmount",
"dataPoints",
"maxValsInArrayIndex",
"isXNumeric",
"isMultipleYAxis",
"isMultiLineX",
"isDataXYZ",
"dataFormatXNumeric",
"allSeriesHasEqualX",
"hasNullValues",
"dataWasParsed",
"hasXaxisGroups",
"hasSeriesGroups",
"skipFirstTimelinelabel",
"skipLastTimelinelabel",
"yValueDecimal",
"invalidLogScale",
"noLabelsProvided"
]) {
proxy(axesNS, key);
}
Object.defineProperty(gl, "axes", {
value: axesNS,
writable: false,
enumerable: false,
configurable: true
});
const layoutNS = {};
for (const key of [
"svgWidth",
"svgHeight",
"gridWidth",
"gridHeight",
"translateX",
"translateY",
"translateXAxisX",
"translateXAxisY",
"translateYAxisX",
"xAxisLabelsHeight",
"xAxisGroupLabelsHeight",
"xAxisLabelsWidth",
"yAxisLabelsWidth",
"yAxisWidths",
"yLabelsCoords",
"yTitleCoords",
"padHorizontal",
"barPadForNumericAxis",
"rotateXLabels",
"scaleX",
"scaleY",
"radialSize",
"defaultLabels",
"overlappingXLabels"
]) {
proxy(layoutNS, key);
}
Object.defineProperty(gl, "layout", {
value: layoutNS,
writable: false,
enumerable: false,
configurable: true
});
const cacheNS = {};
for (const key of [
"domCache",
"dimensionCache",
"cachedSelectors",
"textRectsCache",
"pointsArray",
"dataLabelsRects",
"lastDrawnDataLabelsIndexes",
"delayedElements",
"resizeTimer",
"selectionResizeTimer",
"resizeObserver"
]) {
proxy(cacheNS, key);
}
Object.defineProperty(gl, "cache", {
value: cacheNS,
writable: false,
enumerable: false,
configurable: true
});
}
/**
* Persistent chart state — set ONCE at chart construction and intentionally NOT
* reset by initGlobalVars. These values must survive updateSeries / re-render.
*
* Rule: if a value is recalculated fresh on every render it belongs in
* initGlobalVars instead, not here.
* @returns {import('../../types/internal').ChartGlobals}
* @param {Record<string, any>} config
*/
globalVars(config) {
const globals = {
// ── Identity (set once, never changes) ───────────────────────────────────
chartID: null,
// full chart ID: "apexcharts-<cuid>"
cuid: null,
// random suffix only
// ── Event registry (accumulates listeners, never reset) ───────────────────
events: {
beforeMount: [],
mounted: [],
updated: [],
clicked: [],
selection: [],
dataPointSelection: [],
zoomed: [],
scrolled: []
},
// ── Theme colors (set by Theme module after config merge) ─────────────────
colors: [],
fill: { colors: [] },
stroke: { colors: [] },
dataLabels: { style: { colors: [] } },
radarPolygons: { fill: { colors: [] } },
markers: {
colors: [],
size: config.markers.size,
largestSize: 0
},
// ── Device / environment detected once at startup ─────────────────────────
// Note: isTouchDevice lives on w.interact — see Base.js. Shim installed there.
LINE_HEIGHT_RATIO,
// ── Chart-type flags (derived from config, set during Core.mount) ─────────
axisCharts: true,
// false for pie/radial/treemap etc.
isSlopeChart: config.plotOptions.line.isSlopeChart,
comboCharts: false,
// true when mixing line + column series
// ── Config snapshots (backups for zoom-reset / updateOptions) ────────────
initialConfig: null,
// deep clone of the original user config
initialSeries: [],
lastXAxis: [],
lastYAxis: [],
// ── User interaction state (must survive re-renders) ──────────────────────
// Note: zoomEnabled, panEnabled, selectionEnabled, zoomed, selection,
// visibleXRange, selectedDataPoints, mousedown, clientX, clientY,
// lastClientPosition, lastWheelExecution, capturedSeriesIndex,
// capturedDataPointIndex, disableZoomIn, disableZoomOut, isTouchDevice
// live on w.interact — see Base.js. Backward-compat shims installed there.
// Series collapse state (user-driven, must persist across re-renders)
allSeriesCollapsed: false,
collapsedSeries: [],
collapsedSeriesIndices: [],
ancillaryCollapsedSeries: [],
ancillaryCollapsedSeriesIndices: [],
risingSeries: [],
// series being re-shown after collapse
ignoreYAxisIndexes: [],
// y-axis indices excluded during series collapse
// ── Lifecycle / update flags ──────────────────────────────────────────────
isDirty: false,
// true when user called an update method manually
isExecCalled: false,
// true when update came via exec()
dataChanged: false,
// true when series data was changed dynamically
resized: false,
// true after a container resize
// ── Data format flags (derived from config/series, stable between renders) ─
// Note: dataFormatXNumeric lives on w.axisFlags — see Base.js. Shim installed there.
invalidLogScale: false,
// true when log scale requested but data is invalid
hasNullValues: false,
// true when any series contains null values
// Persistent data tracking
columnSeries: null,
// tracks which series are rendered as bars/columns
yaxis: null,
// resolved yaxis config array
total: 0,
// running total (used by pie/radial)
// ── Animation control ─────────────────────────────────────────────────────
shouldAnimate: true,
previousPaths: [],
// paths from previous render — source for enter animation
// ── SVG viewport (set by Dimensions, but persistent as layout anchor) ─────
svgWidth: 0,
svgHeight: 0,
// Note: gridWidth, gridHeight, translateX, translateY, translateXAxisX,
// translateXAxisY, xAxisLabelsHeight, xAxisGroupLabelsHeight, xAxisLabelsWidth,
// rotateXLabels, xAxisHeight, yLabelsCoords, yTitleCoords live on w.layout —
// see Base.js. Backward-compat shims installed there.
defaultLabels: false,
// Note: formatter properties (xLabelFormatter, yLabelFormatters, etc.) live on
// w.formatters — see Base.js. Backward-compat shims installed there.
yAxisLabelsWidth: 0,
scaleX: 1,
scaleY: 1,
translateYAxisX: [],
yAxisWidths: [],
// ── Instances (created once, replaced only on full re-init) ──────────────
tooltip: null,
resizeObserver: null,
// ── Locale (loaded once; changes only via setLocale()) ───────────────────
locale: {},
// ── Method queue (deferred calls during async operations) ────────────────
memory: {
methodsToExec: []
},
// ── Scale configuration constants — imported from utils/Constants.js ──────
niceScaleAllowedMagMsd: NICE_SCALE_ALLOWED_MAG_MSD,
niceScaleDefaultTicks: NICE_SCALE_DEFAULT_TICKS,
// ── Multi-axis series mapping ─────────────────────────────────────────────
seriesYAxisMap: [],
// yAxis index → series indices[]
seriesYAxisReverseMap: [],
// series index → yAxis index
noData: false
// true when there is nothing to render
};
return (
/** @type {import('../../types/internal').ChartGlobals} */
/** @type {unknown} */
globals
);
}
/**
* @param {Record<string, any>} config
*/
init(config) {
const globals = this.globalVars(config);
this.initGlobalVars(globals);
globals.initialConfig = Utils$1.extend({}, config);
globals.initialSeries = Utils$1.clone(config.series);
globals.lastXAxis = Utils$1.clone(
/** @type {NonNullable<typeof globals.initialConfig>} */
globals.initialConfig.xaxis
);
globals.lastYAxis = Utils$1.clone(
/** @type {NonNullable<typeof globals.initialConfig>} */
globals.initialConfig.yaxis
);
return globals;
}
}
class Base {
/**
* @param {object} opts
*/
constructor(opts) {
this.opts = opts;
}
/**
* Build and return the full chart state object `w`.
* @returns {import('../types/internal').ChartStateW}
*/
init() {
const config = new Config(this.opts).init({ responsiveOverride: false });
const globals = new Globals().init(config);
const w = {
config,
globals,
dom: {},
// DOM node cache — lives here, not inside globals
interact: {
// Tool mode (derived from toolbar config at construction, updated by Toolbar)
zoomEnabled: config.chart.toolbar.autoSelected === "zoom" && config.chart.toolbar.tools.zoom && config.chart.zoom.enabled,
panEnabled: config.chart.toolbar.autoSelected === "pan" && config.chart.toolbar.tools.pan,
selectionEnabled: config.chart.toolbar.autoSelected === "selection" && config.chart.toolbar.tools.selection,
// Zoom / pan state (user-driven, must persist across re-renders)
zoomed: false,
selection: void 0,
visibleXRange: void 0,
selectedDataPoints: [],
// Mouse / pointer state
mousedown: false,
clientX: null,
clientY: null,
lastClientPosition: {},
lastWheelExecution: 0,
// Tooltip capture state
capturedSeriesIndex: -1,
capturedDataPointIndex: -1,
// Timescale zoom bounds (reset per render by TimeScale)
disableZoomIn: false,
disableZoomOut: false,
// Device detection (set once at construction)
isTouchDevice: Environment.isBrowser() ? "ontouchstart" in window || navigator.maxTouchPoints > 0 : false
},
formatters: {
// Populated by Formatters.setLabelFormatters() each render
xLabelFormatter: void 0,
yLabelFormatters: [],
xaxisTooltipFormatter: void 0,
ttKeyFormatter: void 0,
ttVal: void 0,
ttZFormatter: void 0,
legendFormatter: void 0
},
// Candlestick / boxplot OHLC arrays — written by Data.handleCandleStickBoxData()
// each render; empty for all other chart types.
candleData: {
seriesCandleO: [],
seriesCandleH: [],
seriesCandleM: [],
seriesCandleL: [],
seriesCandleC: []
},
// Range chart arrays — written by Data.handleRangeData() each render;
// empty for all other chart types.
rangeData: {
seriesRangeStart: [],
seriesRangeEnd: [],
seriesRange: []
},
// Label / category data — written by Data.parseData() and TimeScale each render.
labelData: {
labels: [],
categoryLabels: [],
timescaleLabels: [],
// written by TimeScale.calculateTimeScaleMinMax()
hasXaxisGroups: false,
groups: [],
seriesGroups: []
},
// Axis / parsing behaviour flags — written by Data.parseData() each render.
axisFlags: {
isXNumeric: false,
dataFormatXNumeric: false,
isDataXYZ: false,
isRangeData: false,
isRangeBar: false,
isMultiLineX: false,
noLabelsProvided: false,
dataWasParsed: false
},
// Parsed series data — written by Data.parseData() each render.
// Note: initialSeries and originalSeries are intentionally excluded —
// they are persistent (survive re-renders) and remain on w.globals.
seriesData: {
series: [],
// main y-values array
seriesNames: [],
seriesX: [],
seriesZ: [],
seriesColors: [],
seriesGoals: [],
stackedSeriesTotals: [],
stackedSeriesTotalsByGroups: []
},
// Grid / axis layout computed by Dimensions.plotCoords() each render.
// gridWidth/gridHeight/translateX/translateY are also used as starting
// points by Dimensions on the next render (accumulated values), so the
// shim must be bidirectional — reads and writes both route correctly.
layout: {
gridHeight: 0,
gridWidth: 0,
translateX: 0,
translateY: 0,
translateXAxisX: 0,
translateXAxisY: 0,
rotateXLabels: false,
xAxisHeight: 0,
xAxisLabelsHeight: 0,
xAxisGroupLabelsHeight: 0,
xAxisLabelsWidth: 0,
yLabelsCoords: [],
yTitleCoords: []
}
};
Object.defineProperty(globals, "dom", {
get() {
return w.dom;
},
set(v) {
w.dom = v;
},
enumerable: false,
configurable: true
});
for (const key of [
"xLabelFormatter",
"yLabelFormatters",
"xaxisTooltipFormatter",
"ttKeyFormatter",
"ttVal",
"ttZFormatter",
"legendFormatter"
]) {
Object.defineProperty(globals, key, {
get() {
return (
/** @type {Record<string,any>} */
w.formatters[key]
);
},
set(v) {
w.formatters[key] = v;
},
enumerable: false,
configurable: true
});
}
for (const key of [
"zoomEnabled",
"panEnabled",
"selectionEnabled",
"zoomed",
"selection",
"visibleXRange",
"selectedDataPoints",
"mousedown",
"clientX",
"clientY",
"lastClientPosition",
"lastWheelExecution",
"capturedSeriesIndex",
"capturedDataPointIndex",
"disableZoomIn",
"disableZoomOut",
"isTouchDevice"
]) {
Object.defineProperty(globals, key, {
get() {
return (
/** @type {Record<string,any>} */
w.interact[key]
);
},
set(v) {
w.interact[key] = v;
},
enumerable: false,
configurable: true
});
}
for (const key of [
"gridHeight",
"gridWidth",
"translateX",
"translateY",
"translateXAxisX",
"translateXAxisY",
"rotateXLabels",
"xAxisHeight",
"xAxisLabelsHeight",
"xAxisGroupLabelsHeight",
"xAxisLabelsWidth",
"yLabelsCoords",
"yTitleCoords"
]) {
Object.defineProperty(globals, key, {
get() {
return (
/** @type {Record<string,any>} */
w.layout[key]
);
},
set(v) {
w.layout[key] = v;
},
enumerable: false,
configurable: true
});
}
for (const key of [
"series",
"seriesNames",
"seriesX",
"seriesZ",
"seriesColors",
"seriesGoals",
"stackedSeriesTotals",
"stackedSeriesTotalsByGroups"
]) {
Object.defineProperty(globals, key, {
get() {
return (
/** @type {Record<string,any>} */
w.seriesData[key]
);
},
set(v) {
w.seriesData[key] = v;
},
enumerable: false,
configurable: true
});
}
for (const key of [
"isXNumeric",
"dataFormatXNumeric",
"isDataXYZ",
"isRangeData",
"isRangeBar",
"isMultiLineX",
"noLabelsProvided",
"dataWasParsed"
]) {
Object.defineProperty(globals, key, {
get() {
return (
/** @type {Record<string,any>} */
w.axisFlags[key]
);
},
set(v) {
w.axisFlags[key] = v;
},
enumerable: false,
configurable: true
});
}
for (const key of [
"labels",
"categoryLabels",
"timescaleLabels",
"hasXaxisGroups",
"groups",
"seriesGroups"
]) {
Object.defineProperty(globals, key, {
get() {
return (
/** @type {Record<string,any>} */
w.labelData[key]
);
},
set(v) {
w.labelData[key] = v;
},
enumerable: false,
configurable: true
});
}
for (const key of ["seriesRangeStart", "seriesRangeEnd", "seriesRange"]) {
Object.defineProperty(globals, key, {
get() {
return (
/** @type {Record<string,any>} */
w.rangeData[key]
);
},
set(v) {
w.rangeData[key] = v;
},
enumerable: false,
configurable: true
});
}
for (const key of [
"seriesCandleO",
"seriesCandleH",
"seriesCandleM",
"seriesCandleL",
"seriesCandleC"
]) {
Object.defineProperty(globals, key, {
get() {
return (
/** @type {Record<string,any>} */
w.candleData[key]
);
},
set(v) {
w.candleData[key] = v;
},
enumerable: false,
configurable: true
});
}
return (
/** @type {import('../types/internal').ChartStateW} */
/** @type {unknown} */
w
);
}
}
class CoreUtils {
/**
* @param {import('../types/internal').ChartStateW} w
*/
constructor(w) {
this.w = w;
}
/**
* @param {any[]} series
* @param {string} chartType
*/
static checkComboSeries(series, chartType) {
let comboCharts = false;
let comboBarCount = 0;
let comboCount = 0;
if (chartType === void 0) {
chartType = "line";
}
if (series.length && typeof series[0].type !== "undefined") {
series.forEach((s) => {
if (s.type === "bar" || s.type === "column" || s.type === "candlestick" || s.type === "boxPlot") {
comboBarCount++;
}
if (typeof s.type !== "undefined" && s.type !== chartType) {
comboCount++;
}
});
}
if (comboCount > 0) {
comboCharts = true;
}
return {
comboBarCount,
comboCharts
};
}
/**
* @memberof CoreUtils
* returns the sum of all individual values in a multiple stacked series
* Eg. w.seriesData.series = [[32,33,43,12], [2,3,5,1]]
* @return [34,36,48,13]
* @param {number[]} excludedSeriesIndices
**/
getStackedSeriesTotals(excludedSeriesIndices = []) {
const w = this.w;
const total = [];
if (w.seriesData.series.length === 0) return total;
for (let i = 0; i < w.seriesData.series[w.globals.maxValsInArrayIndex].length; i++) {
let t = 0;
for (let j = 0; j < w.seriesData.series.length; j++) {
if (typeof w.seriesData.series[j][i] !== "undefined" && excludedSeriesIndices.indexOf(j) === -1) {
t += w.seriesData.series[j][i];
}
}
total.push(t);
}
return total;
}
// get total of the all values inside all series
/**
* @param {number | null} [index]
*/
getSeriesTotalByIndex(index = null) {
if (index === null) {
return (
/** @type {any[]} */
this.w.config.series.reduce(
(acc, cur) => acc + cur,
0
)
);
} else {
return this.w.seriesData.series[index].reduce(
(acc, cur) => acc + cur,
0
);
}
}
/**
* @memberof CoreUtils
* returns the sum of values in a multiple stacked grouped charts
* Eg. w.seriesData.series = [[32,33,43,12], [2,3,5,1], [43, 23, 34, 22]]
* series 1 and 2 are in a group, while series 3 is in another group
* @return [[34, 36, 48, 12], [43, 23, 34, 22]]
**/
getStackedSeriesTotalsByGroups() {
const w = this.w;
const total = [];
w.labelData.seriesGroups.forEach((sg) => {
const includedIndexes = [];
w.config.series.forEach((s, si) => {
if (sg.indexOf(w.seriesData.seriesNames[si]) > -1) {
includedIndexes.push(si);
}
});
const excludedIndices = w.seriesData.series.map((_, fi) => includedIndexes.indexOf(fi) === -1 ? fi : -1).filter((f) => f !== -1);
total.push(this.getStackedSeriesTotals(excludedIndices));
});
return total;
}
setSeriesYAxisMappings() {
const gl = this.w.globals;
const cnf = this.w.config;
let axisSeriesMap = [];
const seriesYAxisReverseMap = [];
const unassignedSeriesIndices = [];
const seriesNameArrayStyle = this.w.seriesData.series.length > cnf.yaxis.length || /**
* @param {ApexYAxis} a
*/
cnf.yaxis.some((a) => Array.isArray(a.seriesName));
cnf.series.forEach((_s, i) => {
unassignedSeriesIndices.push(i);
seriesYAxisReverseMap.push(null);
});
cnf.yaxis.forEach(
(_yaxe, yi) => {
axisSeriesMap[yi] = [];
}
);
const unassignedYAxisIndices = [];
cnf.yaxis.forEach((yaxe, yi) => {
let assigned = false;
if (yaxe.seriesName) {
let seriesNames = [];
if (Array.isArray(yaxe.seriesName)) {
seriesNames = yaxe.seriesName;
} else {
seriesNames.push(yaxe.seriesName);
}
seriesNames.forEach((name2) => {
cnf.series.forEach((s, si) => {
if (
/** @type {any} */
s.name === name2
) {
let remove = si;
if (yi === si || seriesNameArrayStyle) {
if (!seriesNameArrayStyle || unassignedSeriesIndices.indexOf(si) > -1) {
axisSeriesMap[yi].push([yi, si]);
} else {
console.warn(
"Series '" + /** @type {any} */
s.name + "' referenced more than once in what looks like the new style. That is, when using either seriesName: [], or when there are more series than yaxes."
);
}
} else {
axisSeriesMap[si].push([si, yi]);
remove = yi;
}
assigned = true;
remove = unassignedSeriesIndices.indexOf(remove);
if (remove !== -1) {
unassignedSeriesIndices.splice(remove, 1);
}
}
});
});
}
if (!assigned) {
unassignedYAxisIndices.push(yi);
}
});
axisSeriesMap = axisSeriesMap.map((yaxe) => {
const ra = [];
yaxe.forEach((sa) => {
seriesYAxisReverseMap[sa[1]] = sa[0];
ra.push(sa[1]);
});
return ra;
});
let lastUnassignedYAxis = cnf.yaxis.length - 1;
for (let i = 0; i < unassignedYAxisIndices.length; i++) {
lastUnassignedYAxis = unassignedYAxisIndices[i];
axisSeriesMap[lastUnassignedYAxis] = [];
if (unassignedSeriesIndices) {
const si = unassignedSeriesIndices[0];
unassignedSeriesIndices.shift();
axisSeriesMap[lastUnassignedYAxis].push(si);
seriesYAxisReverseMap[si] = lastUnassignedYAxis;
} else {
break;
}
}
unassignedSeriesIndices.forEach((i) => {
axisSeriesMap[lastUnassignedYAxis].push(i);
seriesYAxisReverseMap[i] = lastUnassignedYAxis;
});
gl.seriesYAxisMap = axisSeriesMap.map((x) => x);
gl.seriesYAxisReverseMap = seriesYAxisReverseMap.map((x) => x);
gl.seriesYAxisMap.forEach((axisSeries, ai) => {
axisSeries.forEach((si) => {
if (
/** @type {any} */
cnf.series[si] && /** @type {any} */
cnf.series[si].group === void 0
) {
const _series = (
/** @type {any} */
cnf.series[si]
);
_series.group = "apexcharts-axis-".concat(ai.toString());
}
});
});
}
/**
* @param {number | null} [index]
*/
isSeriesNull(index = null) {
let r = [];
if (index === null) {
r = /** @type {any[]} */
this.w.config.series.filter(
(d) => d !== null
);
} else {
r = /** @type {Record<string,any>} */
this.w.config.series[index].data.filter((d) => d !== null);
}
return r.length === 0;
}
/**
* @param {number} index
*/
seriesHaveSameValues(index) {
return this.w.seriesData.series[index].every(
(val, i, arr) => val === arr[0]
);
}
/**
* @param {any[]} labels
*/
getCategoryLabels(labels) {
const w = this.w;
let catLabels = labels.slice();
if (w.config.xaxis.convertedCatToNumeric) {
catLabels = labels.map((i) => {
return w.config.xaxis.labels.formatter(i - w.globals.minX + 1);
});
}
return catLabels;
}
// maxValsInArrayIndex is the index of series[] which has the largest number of items
getLargestSeries() {
const w = this.w;
w.globals.maxValsInArrayIndex = w.seriesData.series.map((a) => a.length).indexOf(
Math.max.apply(
Math,
/**
* @param {number[]} a
*/
w.seriesData.series.map((a) => a.length)
)
);
}
getLargestMarkerSize() {
const w = this.w;
let size = 0;
w.globals.markers.size.forEach((m) => {
size = Math.max(size, m);
});
if (w.config.markers.discrete && w.config.markers.discrete.length) {
w.config.markers.discrete.forEach((m) => {
size = Math.max(size, m.size);
});
}
if (size > 0) {
if (w.config.markers.hover.size > 0) {
size = w.config.markers.hover.size;
} else {
size += w.config.markers.hover.sizeOffset;
}
}
w.globals.markers.largestSize = size;
return size;
}
/**
* @memberof Core
* returns the sum of all values in a series
* Eg. w.seriesData.series = [[32,33,43,12], [2,3,5,1]]
* @return [120, 11]
**/
getSeriesTotals() {
const w = this.w;
w.globals.seriesTotals = w.seriesData.series.map((ser) => {
let total = 0;
if (Array.isArray(ser)) {
for (let j = 0; j < ser.length; j++) {
total += ser[j];
}
} else {
total += ser;
}
return total;
});
}
/**
* @param {number} minX
* @param {number} maxX
*/
getSeriesTotalsXRange(minX, maxX) {
const w = this.w;
const seriesTotalsXRange = w.seriesData.series.map((ser, index) => {
let total = 0;
for (let j = 0; j < ser.length; j++) {
if (w.seriesData.seriesX[index][j] > minX && w.seriesData.seriesX[index][j] < maxX) {
total += ser[j];
}
}
return total;
});
return seriesTotalsXRange;
}
/**
* @memberof CoreUtils
* returns the percentage value of all individual values which can be used in a 100% stacked series
* Eg. w.seriesData.series = [[32, 33, 43, 12], [2, 3, 5, 1]]
* @return [[94.11, 91.66, 89.58, 92.30], [5.88, 8.33, 10.41, 7.7]]
**/
getPercentSeries() {
const w = this.w;
w.globals.seriesPercent = w.seriesData.series.map((ser) => {
const seriesPercent = [];
if (Array.isArray(ser)) {
for (let j = 0; j < ser.length; j++) {
const total = w.seriesData.stackedSeriesTotals[j];
let percent = 0;
if (total) {
percent = 100 * ser[j] / total;
}
seriesPercent.push(percent);
}
} else {
const total = w.globals.seriesTotals.reduce((acc, val) => acc + val, 0);
const percent = 100 * ser / total;
seriesPercent.push(percent);
}
return seriesPercent;
});
}
getCalculatedRatios() {
const w = this.w;
const gl = w.globals;
const yRatio = [];
let invertedYRatio = 0;
let xRatio = 0;
let invertedXRatio = 0;
let zRatio = 0;
let baseLineY = [];
let baseLineInvertedY = 0.1;
let baseLineX = 0;
gl.yRange = [];
if (gl.isMultipleYAxis) {
for (let i = 0; i < gl.minYArr.length; i++) {
gl.yRange.push(Math.abs(gl.minYArr[i] - gl.maxYArr[i]));
baseLineY.push(0);
}
} else {
gl.yRange.push(Math.abs(gl.minY - gl.maxY));
}
gl.xRange = Math.abs(gl.maxX - gl.minX);
gl.zRange = Math.abs(gl.maxZ - gl.minZ);
for (let i = 0; i < gl.yRange.length; i++) {
yRatio.push(gl.yRange[i] / this.w.layout.gridHeight);
}
xRatio = gl.xRange / this.w.layout.gridWidth;
invertedYRatio = /** @type {any} */
gl.yRange / this.w.layout.gridWidth;
invertedXRatio = gl.xRange / this.w.layout.gridHeight;
zRatio = gl.zRange / this.w.layout.gridHeight * 16;
if (!zRatio) {
zRatio = 1;
}
if (gl.minY !== Number.MIN_VALUE && Math.abs(gl.minY) !== 0) {
const _hasNegsGl = (
/** @type {any} */
gl
);
_hasNegsGl.hasNegs = true;
}
if (w.globals.seriesYAxisReverseMap.length > 0) {
const scaleBaseLineYScale = (y, i) => {
const yAxis = w.config.yaxis[w.globals.seriesYAxisReverseMap[i]];
const sign = y < 0 ? -1 : 1;
y = Math.abs(y);
if (yAxis.logarithmic) {
y = this.getBaseLog(yAxis.logBase, y);
}
return -sign * y / yRatio[i];
};
if (gl.isMultipleYAxis) {
baseLineY = [];
for (let i = 0; i < yRatio.length; i++) {
baseLineY.push(scaleBaseLineYScale(gl.minYArr[i], i));
}
} else {
baseLineY = [];
baseLineY.push(scaleBaseLineYScale(gl.minY, 0));
if (gl.minY !== Number.MIN_VALUE && Math.abs(gl.minY) !== 0) {
baseLineInvertedY = -gl.minY / invertedYRatio;
baseLineX = gl.minX / xRatio;
}
}
} else {
baseLineY = [];
baseLineY.push(0);
baseLineInvertedY = 0;
baseLineX = 0;
}
return {
yRatio,
invertedYRatio,
zRatio,
xRatio,
invertedXRatio,
baseLineInvertedY,
baseLineY,
baseLineX
};
}
/**
* @param {any[]} series
*/
getLogSeries(series) {
const w = this.w;
w.globals.seriesLog = series.map((s, i) => {
const yAxisIndex = w.globals.seriesYAxisReverseMap[i];
if (w.config.yaxis[yAxisIndex] && w.config.yaxis[yAxisIndex].logarithmic) {
return s.map((d) => {
if (d === null) return null;
return this.getLogVal(w.config.yaxis[yAxisIndex].logBase, d, i);
});
} else {
return s;
}
});
return w.globals.invalidLogScale ? series : w.globals.seriesLog;
}
/**
* @param {number} val
* @param {number} seriesIndex
* @returns {number}
*/
getLogValAtSeriesIndex(val, seriesIndex) {
if (val === null) return (
/** @type {any} */
null
);
const w = this.w;
const yAxisIndex = w.globals.seriesYAxisReverseMap[seriesIndex];
if (w.config.yaxis[yAxisIndex] && w.config.yaxis[yAxisIndex].logarithmic) {
return this.getLogVal(
w.config.yaxis[yAxisIndex].logBase,
val,
seriesIndex
);
}
return val;
}
/**
* @param {number} base
* @param {number} value
*/
getBaseLog(base, value) {
return Math.log(value) / Math.log(base);
}
/**
* @param {number} b
* @param {number} d
* @param {number} seriesIndex
*/
getLogVal(b, d, seriesIndex) {
if (d <= 0) {
return 0;
}
const w = this.w;
const min_log_val = w.globals.minYArr[seriesIndex] === 0 ? -1 : this.getBaseLog(b, w.globals.minYArr[seriesIndex]);
const max_log_val = w.globals.maxYArr[seriesIndex] === 0 ? 0 : this.getBaseLog(b, w.globals.maxYArr[seriesIndex]);
const number_of_height_levels = max_log_val - min_log_val;
if (d < 1) return d / number_of_height_levels;
const log_height_value = this.getBaseLog(b, d) - min_log_val;
return log_height_value / number_of_height_levels;
}
/**
* @param {number[]} yRatio
*/
getLogYRatios(yRatio) {
const w = this.w;
const gl = this.w.globals;
const _gl = (
/** @type {any} */
gl
);
_gl.yLogRatio = yRatio.slice();
_gl.logYRange = /** @type {any[]} */
gl.yRange.map(
(_, i) => {
const yAxisIndex = w.globals.seriesYAxisReverseMap[i];
if (w.config.yaxis[yAxisIndex] && this.w.config.yaxis[yAxisIndex].logarithmic) {
let maxY = -Number.MAX_VALUE;
let minY = Number.MIN_VALUE;
let range = 1;
gl.seriesLog.forEach(
(s, si) => {
s.forEach((v) => {
if (w.config.yaxis[si] && w.config.yaxis[si].logarithmic) {
maxY = Math.max(v, maxY);
minY = Math.min(v, minY);
}
});
}
);
range = Math.pow(gl.yRange[i], Math.abs(minY - maxY) / gl.yRange[i]);
_gl.yLogRatio[i] = range / this.w.layout.gridHeight;
return range;
}
}
);
return _gl.invalidLogScale ? yRatio.slice() : _gl.yLogRatio;
}
// Some config objects can be array - and we need to extend them correctly
/**
* @param {any} configInstance
* @param {Record<string, any>} options
* @param {import('../types/internal').ChartStateW} w
*/
static extendArrayProps(configInstance, options2, w) {
var _a, _b;
if (options2 == null ? void 0 : options2.yaxis) {
options2 = configInstance.extendYAxis(options2, w);
}
if (options2 == null ? void 0 : options2.annotations) {
if (options2.annotations.yaxis) {
options2 = configInstance.extendYAxisAnnotations(options2);
}
if ((_a = options2 == null ? void 0 : options2.annotations) == null ? void 0 : _a.xaxis) {
options2 = configInstance.extendXAxisAnnotations(options2);
}
if ((_b = options2 == null ? void 0 : options2.annotations) == null ? void 0 : _b.points) {
options2 = configInstance.extendPointAnnotations(options2);
}
}
return options2;
}
// Series of the same group and type can be stacked together distinct from
// other series of the same type on the same axis.
/**
* @param {Record<string, any>} typeSeries
* @param {string[]} typeGroups
* @param {string} type
* @param {string} chartClass
*/
drawSeriesByGroup(typeSeries, typeGroups, type, chartClass) {
const w = this.w;
const graph = [];
if (typeSeries.series.length > 0) {
typeGroups.forEach((gn) => {
const gs = [];
const gi = [];
typeSeries.i.forEach((i, ii) => {
if (
/** @type {Record<string,any>} */
w.config.series[i].group === gn
) {
gs.push(typeSeries.series[ii]);
gi.push(i);
}
});
gs.length > 0 && graph.push(
/** @type {any} */
chartClass.draw(gs, type, gi)
);
});
}
return graph;
}
}
class Animations {
/**
* @param {import('../types/internal').ChartStateW} w
* @param {import('../types/internal').ChartContext} [ctx]
*/
constructor(w, ctx) {
this.w = w;
this.ctx = ctx;
}
/**
* @param {any} el
* @param {Record<string, any>} from
* @param {Record<string, any>} to
* @param {object} speed
*/
animateLine(el, from, to, speed) {
el.attr(from).animate(speed).attr(to);
}
/*
** Animate radius of a circle element
* @param {any} el
* @param {number} speed
* @param {string} easing
* @param {Function} cb
*/
/** @param {any} el @param {any} speed @param {any} easing @param {any} cb */
animateMarker(el, speed, easing, cb) {
el.attr({
opacity: 0
}).animate(speed).attr({
opacity: 1
}).after(() => {
cb();
});
}
/*
** Animate rect properties
* @param {any} el
* @param {any} from
* @param {any} to
* @param {number} speed
* @param {Function} fn
*/
/** @param {any} el @param {any} from @param {any} to @param {any} speed @param {any} fn */
animateRect(el, from, to, speed, fn) {
el.attr(from).animate(speed).attr(to).after(() => fn());
}
/**
* @param {Record<string, any>} params
*/
animatePathsGradually(params) {
const { el, realIndex, j, fill, pathFrom, pathTo, speed, delay } = params;
const me = this;
const w = this.w;
let delayFactor = 0;
if (w.config.chart.animations.animateGradually.enabled) {
delayFactor = w.config.chart.animations.animateGradually.delay;
}
if (w.config.chart.animations.dynamicAnimation.enabled && w.globals.dataChanged && w.config.chart.type !== "bar") {
delayFactor = 0;
}
me.morphSVG(
el,
realIndex,
j,
w.config.chart.type === "line" && !w.globals.comboCharts ? "stroke" : fill,
pathFrom,
pathTo,
speed,
delay * delayFactor
);
}
showDelayedElements() {
this.w.globals.delayedElements.forEach((d) => {
const ele = d.el;
ele.classList.remove("apexcharts-element-hidden");
ele.classList.add("apexcharts-hidden-element-shown");
});
}
/**
* @param {any} el
*/
animationCompleted(el) {
const w = this.w;
if (w.globals.animationEnded) return;
w.globals.animationEnded = true;
this.showDelayedElements();
if (typeof w.config.chart.events.animationEnd === "function") {
w.config.chart.events.animationEnd(this.ctx, { el, w });
}
}
// SVG.js animation for morphing one path to another
/**
* @param {any} el
* @param {number} realIndex
* @param {number} j
* @param {string} fill
* @param {string} pathFrom
* @param {string} pathTo
* @param {number} speed
* @param {number} delay
*/
morphSVG(el, realIndex, j, fill, pathFrom, pathTo, speed, delay) {
const w = this.w;
if (!pathFrom) {
pathFrom = el.attr("pathFrom");
}
if (!pathTo) {
pathTo = el.attr("pathTo");
}
const disableAnimationForCorrupPath = () => {
if (w.config.chart.type === "radar") {
speed = 1;
}
return `M 0 ${w.layout.gridHeight}`;
};
if (!pathFrom || pathFrom.indexOf("undefined") > -1 || pathFrom.indexOf("NaN") > -1) {
pathFrom = disableAnimationForCorrupPath();
}
if (!pathTo.trim() || pathTo.indexOf("undefined") > -1 || pathTo.indexOf("NaN") > -1) {
pathTo = disableAnimationForCorrupPath();
}
if (!w.globals.shouldAnimate) {
speed = 1;
}
el.plot(pathFrom).animate(1, delay).plot(pathFrom).animate(speed, delay).plot(pathTo).after(() => {
if (Utils$1.isNumber(j)) {
if (j === w.seriesData.series[w.globals.maxValsInArrayIndex].length - 2 && w.globals.shouldAnimate) {
this.animationCompleted(el);
}
} else if (fill !== "none" && w.globals.shouldAnimate) {
if (!w.globals.comboCharts && realIndex === w.seriesData.series.length - 1 || w.globals.comboCharts) {
this.animationCompleted(el);
}
}
this.showDelayedElements();
});
}
}
class Filters {
/**
* @param {import('../types/internal').ChartStateW} w
*/
constructor(w) {
this.w = w;
}
// create a re-usable filter which can be appended other filter effects and applied to multiple elements
/**
* @param {any} el
* @param {number} i
*/
getDefaultFilter(el, i) {
const w = this.w;
if (el.unfilter) {
el.unfilter(true);
}
if (w.config.chart.dropShadow.enabled) {
this.dropShadow(el, w.config.chart.dropShadow, i);
}
}
/**
* @param {any} el
* @param {number} i
* @param {string} filterType
*/
applyFilter(el, i, filterType) {
var _a, _b, _c;
const w = this.w;
if (el.unfilter) {
el.unfilter(true);
}
if (filterType === "none") {
this.getDefaultFilter(el, i);
return;
}
const shadowAttr = w.config.chart.dropShadow;
const brightnessFactor = filterType === "lighten" ? 2 : 0.3;
if (el.filterWith) {
el.filterWith((add) => {
add.colorMatrix({
type: "matrix",
values: `
${brightnessFactor} 0 0 0 0
0 ${brightnessFactor} 0 0 0
0 0 ${brightnessFactor} 0 0
0 0 0 1 0
`,
in: "SourceGraphic",
result: "brightness"
});
if (shadowAttr.enabled) {
this.addShadow(add, i, shadowAttr, "brightness");
}
});
if (!shadowAttr.noUserSpaceOnUse) {
(_b = (_a = el.filterer()) == null ? void 0 : _a.node) == null ? void 0 : _b.setAttribute("filterUnits", "userSpaceOnUse");
}
this._scaleFilterSize((_c = el.filterer()) == null ? void 0 : _c.node);
}
}
// appends dropShadow to the filter object which can be chained with other filter effects
/**
* @param {any} add
* @param {number} i
* @param {Record<string, any>} attrs
* @param {string} source
*/
addShadow(add, i, attrs, source) {
var _a;
const w = this.w;
let { blur, top, left, color, opacity } = attrs;
color = Array.isArray(color) ? color[i] : color;
if (((_a = w.config.chart.dropShadow.enabledOnSeries) == null ? void 0 : _a.length) > 0) {
if (w.config.chart.dropShadow.enabledOnSeries.indexOf(i) === -1) {
return add;
}
}
add.offset({
in: source,
dx: left,
dy: top,
result: "offset"
});
add.gaussianBlur({
in: "offset",
stdDeviation: blur,
result: "blur"
});
add.flood({
"flood-color": color,
"flood-opacity": opacity,
result: "flood"
});
add.composite({
in: "flood",
in2: "blur",
operator: "in",
result: "shadow"
});
add.merge(["shadow", source]);
}
// directly adds dropShadow to the element and returns the same element.
/**
* @param {any} el
* @param {Record<string, any>} attrs
*/
dropShadow(el, attrs, i = 0) {
var _a, _b, _c, _d, _e;
const w = this.w;
if (el.unfilter) {
el.unfilter(true);
}
if (Utils$1.isMsEdge() && w.config.chart.type === "radialBar") {
return el;
}
if (((_a = w.config.chart.dropShadow.enabledOnSeries) == null ? void 0 : _a.length) > 0) {
if (((_b = w.config.chart.dropShadow.enabledOnSeries) == null ? void 0 : _b.indexOf(i)) === -1) {
return el;
}
}
if (el.filterWith) {
el.filterWith((add) => {
this.addShadow(add, i, attrs, "SourceGraphic");
});
if (!attrs.noUserSpaceOnUse) {
(_d = (_c = el.filterer()) == null ? void 0 : _c.node) == null ? void 0 : _d.setAttribute("filterUnits", "userSpaceOnUse");
}
this._scaleFilterSize((_e = el.filterer()) == null ? void 0 : _e.node);
}
return el;
}
/**
* @param {any} el
* @param {number} realIndex
* @param {number} dataPointIndex
*/
setSelectionFilter(el, realIndex, dataPointIndex) {
const w = this.w;
if (typeof w.interact.selectedDataPoints[realIndex] !== "undefined") {
if (w.interact.selectedDataPoints[realIndex].indexOf(dataPointIndex) > -1) {
el.node.setAttribute("selected", true);
const activeFilter = w.config.states.active.filter;
if (activeFilter !== "none") {
this.applyFilter(el, realIndex, activeFilter.type);
}
}
}
}
/**
* @param {any} el
*/
_scaleFilterSize(el) {
if (!el) return;
const setAttributes = (attrs) => {
for (const key in attrs) {
if (Object.prototype.hasOwnProperty.call(attrs, key)) {
el.setAttribute(key, attrs[key]);
}
}
};
setAttributes({
width: "200%",
height: "200%",
x: "-50%",
y: "-50%"
});
}
}
class Graphics {
/**
* @param {import('../types/internal').ChartStateW} w
* @param {import('../types/internal').ChartContext | null} ctx
*/
constructor(w, ctx = null) {
this.w = w;
this.ctx = ctx;
}
/*****************************************************************************
* *
* SVG Path Rounding Function *
* Copyright (C) 2014 Yona Appletree *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* *
*****************************************************************************/
/**
* SVG Path rounding function. Takes an input path string and outputs a path
* string where all line-line corners have been rounded. Only supports absolute
* commands at the moment.
*
* @param pathString The SVG input path
* @param radius The amount to round the corners, either a value in the SVG
* coordinate space, or, if useFractionalRadius is true, a value
* from 0 to 1.
* @returns A new SVG path string with the rounding
* @param {string} pathString
* @param {number} radius
*/
roundPathCorners(pathString, radius) {
if (pathString.indexOf("NaN") > -1) pathString = "";
function moveTowardsLength(movingPoint, targetPoint, amount) {
var width = targetPoint.x - movingPoint.x;
var height = targetPoint.y - movingPoint.y;
var distance = Math.sqrt(width * width + height * height);
return moveTowardsFractional(
movingPoint,
targetPoint,
Math.min(1, amount / distance)
);
}
function moveTowardsFractional(movingPoint, targetPoint, fraction) {
return {
x: movingPoint.x + (targetPoint.x - movingPoint.x) * fraction,
y: movingPoint.y + (targetPoint.y - movingPoint.y) * fraction
};
}
function adjustCommand(cmd, newPoint) {
if (cmd.length > 2) {
cmd[cmd.length - 2] = newPoint.x;
cmd[cmd.length - 1] = newPoint.y;
}
}
function pointForCommand(cmd) {
return {
x: parseFloat(cmd[cmd.length - 2]),
y: parseFloat(cmd[cmd.length - 1])
};
}
var pathParts = pathString.split(/[,\s]/).reduce(function(parts, part) {
var match = part.match(/^([a-zA-Z])(.+)/);
if (match) {
parts.push(match[1]);
parts.push(match[2]);
} else {
parts.push(part);
}
return parts;
}, []);
var commands = pathParts.reduce(function(commands2, part) {
if (parseFloat(part) == part && commands2.length) {
commands2[commands2.length - 1].push(part);
} else {
commands2.push([part]);
}
return commands2;
}, []);
var resultCommands = [];
if (commands.length > 1) {
var startPoint = pointForCommand(commands[0]);
var virtualCloseLine = null;
if (commands[commands.length - 1][0] == "Z" && commands[0].length > 2) {
virtualCloseLine = ["L", startPoint.x, startPoint.y];
commands[commands.length - 1] = virtualCloseLine;
}
resultCommands.push(commands[0]);
for (var cmdIndex = 1; cmdIndex < commands.length; cmdIndex++) {
var prevCmd = resultCommands[resultCommands.length - 1];
var curCmd = commands[cmdIndex];
var nextCmd = curCmd == virtualCloseLine ? commands[1] : commands[cmdIndex + 1];
if (nextCmd && prevCmd && prevCmd.length > 2 && curCmd[0] == "L" && nextCmd.length > 2 && nextCmd[0] == "L") {
var prevPoint = pointForCommand(prevCmd);
var curPoint = pointForCommand(curCmd);
var nextPoint = pointForCommand(nextCmd);
var curveStart, curveEnd;
curveStart = moveTowardsLength(curPoint, prevPoint, radius);
curveEnd = moveTowardsLength(curPoint, nextPoint, radius);
adjustCommand(curCmd, curveStart);
curCmd.origPoint = curPoint;
resultCommands.push(curCmd);
var startControl = moveTowardsFractional(curveStart, curPoint, 0.5);
var endControl = moveTowardsFractional(curPoint, curveEnd, 0.5);
var curveCmd = [
"C",
startControl.x,
startControl.y,
endControl.x,
endControl.y,
curveEnd.x,
curveEnd.y
];
curveCmd.origPoint = curPoint;
resultCommands.push(curveCmd);
} else {
resultCommands.push(curCmd);
}
}
if (virtualCloseLine) {
var newStartPoint = pointForCommand(
resultCommands[resultCommands.length - 1]
);
resultCommands.push(["Z"]);
adjustCommand(resultCommands[0], newStartPoint);
}
} else {
resultCommands = commands;
}
return resultCommands.reduce(function(str, c) {
return str + c.join(" ") + " ";
}, "");
}
/**
* @param {number} x1
* @param {number} y1
* @param {number} x2
* @param {number} y2
* @param {number | null} [strokeWidth]
*/
drawLine(x1, y1, x2, y2, lineColor = "#a8a8a8", dashArray = 0, strokeWidth = null, strokeLineCap = "butt") {
const w = this.w;
const line = w.dom.Paper.line().attr({
x1,
y1,
x2,
y2,
stroke: lineColor,
"stroke-dasharray": dashArray,
"stroke-width": strokeWidth,
"stroke-linecap": strokeLineCap
});
return line;
}
/**
* @param {number | null} [strokeWidth]
* @param {string | null} [strokeColor]
*/
drawRect(x1 = 0, y1 = 0, x2 = 0, y2 = 0, radius = 0, color = "#fefefe", opacity = 1, strokeWidth = null, strokeColor = null, strokeDashArray = 0) {
const w = this.w;
const rect = w.dom.Paper.rect();
rect.attr({
x: x1,
y: y1,
width: x2 > 0 ? x2 : 0,
height: y2 > 0 ? y2 : 0,
rx: radius,
ry: radius,
opacity,
"stroke-width": strokeWidth !== null ? strokeWidth : 0,
stroke: strokeColor !== null ? strokeColor : "none",
"stroke-dasharray": strokeDashArray
});
rect.node.setAttribute("fill", color);
return rect;
}
/**
* @param {string} polygonString
*/
drawPolygon(polygonString, stroke = "#e1e1e1", strokeWidth = 1, fill = "none") {
const w = this.w;
const polygon = w.dom.Paper.polygon(polygonString).attr({
fill,
stroke,
"stroke-width": strokeWidth
});
return polygon;
}
/**
* @param {number} radius
* @param {Record<string, any> | null} attrs
*/
drawCircle(radius, attrs = null) {
const w = this.w;
if (radius < 0) radius = 0;
const c = w.dom.Paper.circle(radius * 2);
if (attrs !== null) {
c.attr(attrs);
}
return c;
}
/** @param {{ d?: string, stroke?: string, strokeWidth?: number, fill: any, fillOpacity?: number, strokeOpacity?: number, classes?: any, strokeLinecap?: any, strokeDashArray?: number }} opts */
drawPath({
d = "",
stroke = "#a8a8a8",
strokeWidth = 1,
fill,
fillOpacity = 1,
strokeOpacity = 1,
classes,
strokeLinecap = null,
strokeDashArray = 0
}) {
const w = this.w;
if (strokeLinecap === null) {
strokeLinecap = w.config.stroke.lineCap;
}
if (d.indexOf("undefined") > -1 || d.indexOf("NaN") > -1) {
d = `M 0 ${w.layout.gridHeight}`;
}
const p = w.dom.Paper.path(d).attr({
fill,
"fill-opacity": fillOpacity,
stroke,
"stroke-opacity": strokeOpacity,
"stroke-linecap": strokeLinecap,
"stroke-width": strokeWidth,
"stroke-dasharray": strokeDashArray,
class: classes
});
return p;
}
/**
* @param {Record<string, any> | null} attrs
*/
group(attrs = null) {
const w = this.w;
const g = w.dom.Paper.group();
if (attrs !== null) {
g.attr(attrs);
}
return g;
}
/**
* @param {number} x
* @param {number} y
*/
move(x, y) {
const move = ["M", x, y].join(" ");
return move;
}
/**
* @param {number | null} x
* @param {number | null} y
* @param {string | null} hORv
* @returns {string}
*/
line(x, y, hORv = null) {
if (hORv === "H") return [" H", x].join(" ");
if (hORv === "V") return [" V", y].join(" ");
return [" L", x, y].join(" ");
}
/**
* @param {number} x1
* @param {number} y1
* @param {number} x2
* @param {number} y2
* @param {number} x
* @param {number} y
*/
curve(x1, y1, x2, y2, x, y) {
const curve = ["C", x1, y1, x2, y2, x, y].join(" ");
return curve;
}
/**
* @param {number} x1
* @param {number} y1
* @param {number} x
* @param {number} y
*/
quadraticCurve(x1, y1, x, y) {
const curve = ["Q", x1, y1, x, y].join(" ");
return curve;
}
/**
* @param {number} rx
* @param {number} ry
* @param {number} axisRotation
* @param {number} largeArcFlag
* @param {number} sweepFlag
* @param {number} x
* @param {number} y
*/
arc(rx, ry, axisRotation, largeArcFlag, sweepFlag, x, y, relative = false) {
let coord = "A";
if (relative) coord = "a";
const arc = [
coord,
rx,
ry,
axisRotation,
largeArcFlag,
sweepFlag,
x,
y
].join(" ");
return arc;
}
/**
* @memberof Graphics
* @param {Record<string, any>} opts
* i = series's index
* realIndex = realIndex is series's actual index when it was drawn time. After several redraws, the iterating "i" may change in loops, but realIndex doesn't
* pathFrom = existing pathFrom to animateTo
* pathTo = new Path to which d attr will be animated from pathFrom to pathTo
* stroke = line Color
* strokeWidth = width of path Line
* fill = it can be gradient, single color, pattern or image
* animationDelay = how much to delay when starting animation (in milliseconds)
* dataChangeSpeed = for dynamic animations, when data changes
* className = class attribute to add
* @return {any} svg.js path object
**/
renderPaths({
j,
realIndex,
pathFrom,
pathTo,
stroke,
strokeWidth,
strokeLinecap,
fill,
animationDelay,
initialSpeed,
dataChangeSpeed,
className,
chartType,
shouldClipToGrid = true,
bindEventsOnPaths = true,
drawShadow = true
}) {
const w = this.w;
const filters = new Filters(this.w);
const anim = new Animations(
this.w,
/** @type {any} */
void 0
);
const initialAnim = this.w.config.chart.animations.enabled;
const dynamicAnim = initialAnim && this.w.config.chart.animations.dynamicAnimation.enabled;
if (pathFrom && pathFrom.startsWith("M 0 0") && pathTo) {
const moveCommand = pathTo.match(/^M\s+[\d.-]+\s+[\d.-]+/);
if (moveCommand) {
pathFrom = pathFrom.replace(/^M\s+0\s+0/, moveCommand[0]);
}
}
let d;
const shouldAnimate = !!(initialAnim && !w.globals.resized || dynamicAnim && w.globals.dataChanged && w.globals.shouldAnimate);
if (shouldAnimate) {
d = pathFrom;
} else {
d = pathTo;
w.globals.animationEnded = true;
}
const strokeDashArrayOpt = w.config.stroke.dashArray;
let strokeDashArray = 0;
if (Array.isArray(strokeDashArrayOpt)) {
strokeDashArray = strokeDashArrayOpt[realIndex];
} else {
strokeDashArray = w.config.stroke.dashArray;
}
const el = this.drawPath({
d,
stroke,
strokeWidth,
fill,
fillOpacity: 1,
classes: className,
strokeLinecap,
strokeDashArray
});
el.attr("index", realIndex);
if (shouldClipToGrid) {
if (chartType === "bar" && !w.globals.isBarHorizontal || w.globals.comboCharts) {
el.attr({
"clip-path": `url(#gridRectBarMask${w.globals.cuid})`
});
} else {
el.attr({
"clip-path": `url(#gridRectMask${w.globals.cuid})`
});
}
}
if (w.config.chart.dropShadow.enabled && drawShadow) {
filters.dropShadow(el, w.config.chart.dropShadow, realIndex);
}
if (bindEventsOnPaths) {
el.node.addEventListener("mouseenter", this.pathMouseEnter.bind(this, el));
el.node.addEventListener("mouseleave", this.pathMouseLeave.bind(this, el));
el.node.addEventListener("mousedown", this.pathMouseDown.bind(this, el));
}
el.attr({
pathTo,
pathFrom
});
const defaultAnimateOpts = {
el,
j,
realIndex,
pathFrom,
pathTo,
fill,
strokeWidth,
delay: animationDelay
};
if (initialAnim && !w.globals.resized && !w.globals.dataChanged) {
anim.animatePathsGradually(__spreadProps(__spreadValues({}, defaultAnimateOpts), {
speed: initialSpeed
}));
} else {
if (w.globals.resized || !w.globals.dataChanged) {
anim.showDelayedElements();
}
}
if (w.globals.dataChanged && dynamicAnim && shouldAnimate) {
anim.animatePathsGradually(__spreadProps(__spreadValues({}, defaultAnimateOpts), {
speed: dataChangeSpeed
}));
}
return el;
}
/**
* @param {string} style
* @param {number} width
* @param {number} height
*/
drawPattern(style, width, height, stroke = "#a8a8a8", strokeWidth = 0) {
const w = this.w;
const p = w.dom.Paper.pattern(width, height, (add) => {
if (style === "horizontalLines") {
add.line(0, 0, height, 0).stroke({ color: stroke, width: strokeWidth + 1 });
} else if (style === "verticalLines") {
add.line(0, 0, 0, width).stroke({ color: stroke, width: strokeWidth + 1 });
} else if (style === "slantedLines") {
add.line(0, 0, width, height).stroke({ color: stroke, width: strokeWidth });
} else if (style === "squares") {
add.rect(width, height).fill("none").stroke({ color: stroke, width: strokeWidth });
} else if (style === "circles") {
add.circle(width).fill("none").stroke({ color: stroke, width: strokeWidth });
}
});
return p;
}
/**
* @param {string} style
* @param {string} gfrom
* @param {string} gto
* @param {number} opacityFrom
* @param {number} opacityTo
* @param {number | null} [size]
* @param {number[] | null} stops
* @param {any[]} colorStops
*/
drawGradient(style, gfrom, gto, opacityFrom, opacityTo, size = null, stops = null, colorStops = [], i = 0) {
const w = this.w;
let g;
if (gfrom.length < 9 && gfrom.indexOf("#") === 0) {
gfrom = Utils$1.hexToRgba(gfrom, opacityFrom);
}
if (gto.length < 9 && gto.indexOf("#") === 0) {
gto = Utils$1.hexToRgba(gto, opacityTo);
}
let stop1 = 0;
let stop2 = 1;
let stop3 = 1;
let stop4 = null;
if (stops !== null) {
stop1 = typeof stops[0] !== "undefined" ? stops[0] / 100 : 0;
stop2 = typeof stops[1] !== "undefined" ? stops[1] / 100 : 1;
stop3 = typeof stops[2] !== "undefined" ? stops[2] / 100 : 1;
stop4 = typeof stops[3] !== "undefined" ? stops[3] / 100 : null;
}
const radial = !!(w.config.chart.type === "donut" || w.config.chart.type === "pie" || w.config.chart.type === "polarArea" || w.config.chart.type === "bubble");
if (!colorStops || colorStops.length === 0) {
g = w.dom.Paper.gradient(
radial ? "radial" : "linear",
(add) => {
add.stop(stop1, gfrom, opacityFrom);
add.stop(stop2, gto, opacityTo);
add.stop(stop3, gto, opacityTo);
if (stop4 !== null) {
add.stop(stop4, gfrom, opacityFrom);
}
}
);
} else {
g = w.dom.Paper.gradient(
radial ? "radial" : "linear",
(add) => {
const gradientStops = Array.isArray(colorStops[i]) ? colorStops[i] : colorStops;
gradientStops.forEach((s) => {
add.stop(s.offset / 100, s.color, s.opacity);
});
}
);
}
if (!radial) {
if (style === "vertical") {
g.from(0, 0).to(0, 1);
} else if (style === "diagonal") {
g.from(0, 0).to(1, 1);
} else if (style === "horizontal") {
g.from(0, 1).to(1, 1);
} else if (style === "diagonal2") {
g.from(1, 0).to(0, 1);
}
} else {
const offx = w.layout.gridWidth / 2;
const offy = w.layout.gridHeight / 2;
if (w.config.chart.type !== "bubble") {
g.attr({
gradientUnits: "userSpaceOnUse",
cx: offx,
cy: offy,
r: size
});
} else {
g.attr({
cx: 0.5,
cy: 0.5,
r: 0.8,
fx: 0.2,
fy: 0.2
});
}
}
return g;
}
/** @param {{ text: any, maxWidth: any, fontSize: any, fontFamily?: any }} opts */
getTextBasedOnMaxWidth({ text, maxWidth, fontSize, fontFamily }) {
const tRects = this.getTextRects(text, fontSize, fontFamily, "");
const wordWidth = tRects.width / text.length;
const wordsBasedOnWidth = Math.floor(maxWidth / wordWidth);
if (maxWidth < tRects.width) {
return text.slice(0, wordsBasedOnWidth - 3) + "...";
}
return text;
}
/**
* @param {{ x: any, y: any, text: any, textAnchor?: any, fontSize?: any, fontFamily?: any, fontWeight?: any, foreColor?: any, opacity?: any, maxWidth?: any, cssClass?: string, isPlainText?: boolean, dominantBaseline?: string }} opts
*/
drawText({
x,
y,
text,
textAnchor,
fontSize,
fontFamily,
fontWeight,
foreColor,
opacity,
maxWidth,
cssClass = "",
isPlainText = true,
dominantBaseline = "auto"
}) {
const w = this.w;
if (typeof text === "undefined") text = "";
let truncatedText = text;
if (!textAnchor) {
textAnchor = "start";
}
if (!foreColor || !foreColor.length) {
foreColor = w.config.chart.foreColor;
}
fontFamily = fontFamily || w.config.chart.fontFamily;
fontSize = fontSize || "11px";
fontWeight = fontWeight || "regular";
const commonProps = {
maxWidth,
fontSize,
fontFamily
};
let elText;
if (Array.isArray(text)) {
elText = w.dom.Paper.text((add) => {
for (let i = 0; i < text.length; i++) {
truncatedText = text[i];
if (maxWidth) {
truncatedText = this.getTextBasedOnMaxWidth(__spreadValues({
text: text[i]
}, commonProps));
}
i === 0 ? add.tspan(truncatedText) : add.tspan(truncatedText).newLine();
}
});
} else {
if (maxWidth) {
truncatedText = this.getTextBasedOnMaxWidth(__spreadValues({
text
}, commonProps));
}
elText = isPlainText ? w.dom.Paper.plain(text) : (
/**
* @param {any} add
*/
w.dom.Paper.text((add) => add.tspan(truncatedText))
);
}
elText.attr({
x,
y,
"text-anchor": textAnchor,
"dominant-baseline": dominantBaseline,
"font-size": fontSize,
"font-family": fontFamily,
"font-weight": fontWeight,
fill: foreColor,
class: "apexcharts-text " + cssClass
});
elText.node.style.fontFamily = fontFamily;
elText.node.style.opacity = opacity;
return elText;
}
/**
* @param {number} x
* @param {number} y
* @param {string} type
* @param {number} size
*/
getMarkerPath(x, y, type, size) {
let d = "";
switch (type) {
case "cross":
size = size / 1.4;
d = `M ${x - size} ${y - size} L ${x + size} ${y + size} M ${x - size} ${y + size} L ${x + size} ${y - size}`;
break;
case "plus":
size = size / 1.12;
d = `M ${x - size} ${y} L ${x + size} ${y} M ${x} ${y - size} L ${x} ${y + size}`;
break;
case "star":
case "sparkle": {
let points = 5;
size = size * 1.15;
if (type === "sparkle") {
size = size / 1.1;
points = 4;
}
const step = Math.PI / points;
for (let i = 0; i <= 2 * points; i++) {
const angle = i * step;
const radius = i % 2 === 0 ? size : size / 2;
const xPos = x + radius * Math.sin(angle);
const yPos = y - radius * Math.cos(angle);
d += (i === 0 ? "M" : "L") + xPos + "," + yPos;
}
d += "Z";
break;
}
case "triangle":
d = `M ${x} ${y - size}
L ${x + size} ${y + size}
L ${x - size} ${y + size}
Z`;
break;
case "square":
case "rect":
size = size / 1.125;
d = `M ${x - size} ${y - size}
L ${x + size} ${y - size}
L ${x + size} ${y + size}
L ${x - size} ${y + size}
Z`;
break;
case "diamond":
size = size * 1.05;
d = `M ${x} ${y - size}
L ${x + size} ${y}
L ${x} ${y + size}
L ${x - size} ${y}
Z`;
break;
case "line":
size = size / 1.1;
d = `M ${x - size} ${y}
L ${x + size} ${y}`;
break;
case "circle":
default:
size = size * 2;
d = `M ${x}, ${y}
m -${size / 2}, 0
a ${size / 2},${size / 2} 0 1,0 ${size},0
a ${size / 2},${size / 2} 0 1,0 -${size},0`;
break;
}
return d;
}
/**
* @param {number} x - The x-coordinate of the marker
* @param {number} y - The y-coordinate of the marker
* @param {string} type - Marker shape type
* @param {number} size - The size of the marker
* @param {Record<string, any>} opts - The options for the marker
* @returns {any} The created marker.
*/
drawMarkerShape(x, y, type, size, opts) {
const path = this.drawPath({
d: this.getMarkerPath(x, y, type, size),
stroke: opts.pointStrokeColor,
strokeDashArray: opts.pointStrokeDashArray,
strokeWidth: opts.pointStrokeWidth,
fill: opts.pointFillColor,
fillOpacity: opts.pointFillOpacity,
strokeOpacity: opts.pointStrokeOpacity
});
path.attr({
cx: x,
cy: y,
shape: opts.shape,
class: opts.class ? opts.class : ""
});
return path;
}
/**
* @param {number} x
* @param {number} y
* @param {Record<string, any>} opts
*/
drawMarker(x, y, opts) {
x = x || 0;
let size = opts.pSize || 0;
if (!Utils$1.isNumber(y)) {
size = 0;
y = 0;
}
return this.drawMarkerShape(x, y, opts == null ? void 0 : opts.shape, size, __spreadValues(__spreadValues({}, opts), opts.shape === "line" || opts.shape === "plus" || opts.shape === "cross" ? {
pointStrokeColor: opts.pointFillColor,
pointStrokeOpacity: opts.pointFillOpacity
} : {}));
}
/**
* @param {any} path
* @param {Event | null} [e]
*/
pathMouseEnter(path, e) {
var _a, _b;
const w = this.w;
const filters = new Filters(this.w);
const i = parseInt((_a = path.node.getAttribute("index")) != null ? _a : "", 10);
const j = parseInt((_b = path.node.getAttribute("j")) != null ? _b : "", 10);
if (isNaN(i) || isNaN(j)) return;
if (typeof w.config.chart.events.dataPointMouseEnter === "function") {
w.config.chart.events.dataPointMouseEnter(e, this.ctx, {
seriesIndex: i,
dataPointIndex: j,
w
});
}
Graphics._fireEvent(w, "dataPointMouseEnter", [
e,
this.ctx,
{ seriesIndex: i, dataPointIndex: j, w }
]);
if (w.config.states.active.filter.type !== "none") {
if (path.node.getAttribute("selected") === "true") {
return;
}
}
if (w.config.states.hover.filter.type !== "none") {
if (!w.interact.isTouchDevice) {
const hoverFilter = w.config.states.hover.filter;
filters.applyFilter(path, i, hoverFilter.type);
}
}
}
/**
* @param {any} path
* @param {Event | null} [e]
*/
pathMouseLeave(path, e) {
var _a, _b;
const w = this.w;
const filters = new Filters(this.w);
const i = parseInt((_a = path.node.getAttribute("index")) != null ? _a : "", 10);
const j = parseInt((_b = path.node.getAttribute("j")) != null ? _b : "", 10);
if (isNaN(i) || isNaN(j)) return;
if (typeof w.config.chart.events.dataPointMouseLeave === "function") {
w.config.chart.events.dataPointMouseLeave(e, this.ctx, {
seriesIndex: i,
dataPointIndex: j,
w
});
}
Graphics._fireEvent(w, "dataPointMouseLeave", [
e,
this.ctx,
{ seriesIndex: i, dataPointIndex: j, w }
]);
if (w.config.states.active.filter.type !== "none") {
if (path.node.getAttribute("selected") === "true") {
return;
}
}
if (w.config.states.hover.filter.type !== "none") {
filters.getDefaultFilter(path, i);
}
}
/**
* @param {any} path
* @param {Event | null} e
*/
pathMouseDown(path, e) {
var _a, _b;
const w = this.w;
const filters = new Filters(this.w);
const i = parseInt((_a = path.node.getAttribute("index")) != null ? _a : "", 10);
const j = parseInt((_b = path.node.getAttribute("j")) != null ? _b : "", 10);
if (isNaN(i) || isNaN(j)) return;
let selected = "false";
if (path.node.getAttribute("selected") === "true") {
path.node.setAttribute("selected", "false");
const index = w.interact.selectedDataPoints[i].indexOf(j);
if (index > -1) {
w.interact.selectedDataPoints[i].splice(index, 1);
}
} else {
if (!w.config.states.active.allowMultipleDataPointsSelection && w.interact.selectedDataPoints.length > 0) {
w.interact.selectedDataPoints = [];
const elPaths = w.dom.Paper.find(
".apexcharts-series path:not(.apexcharts-decoration-element)"
);
const elCircles = w.dom.Paper.find(
".apexcharts-series circle:not(.apexcharts-decoration-element), .apexcharts-series rect:not(.apexcharts-decoration-element)"
);
const deSelect = (els) => {
Array.prototype.forEach.call(els, (el) => {
el.node.setAttribute("selected", "false");
filters.getDefaultFilter(el, i);
});
};
deSelect(elPaths);
deSelect(elCircles);
}
path.node.setAttribute("selected", "true");
selected = "true";
if (typeof w.interact.selectedDataPoints[i] === "undefined") {
w.interact.selectedDataPoints[i] = [];
}
w.interact.selectedDataPoints[i].push(j);
}
if (selected === "true") {
const activeFilter = w.config.states.active.filter;
if (activeFilter !== "none") {
filters.applyFilter(path, i, activeFilter.type);
} else {
if (w.config.states.hover.filter !== "none") {
if (!w.interact.isTouchDevice) {
const hoverFilter = w.config.states.hover.filter;
filters.applyFilter(path, i, hoverFilter.type);
}
}
}
} else {
if (w.config.states.active.filter.type !== "none") {
if (w.config.states.hover.filter.type !== "none" && !w.interact.isTouchDevice) {
const hoverFilter = w.config.states.hover.filter;
filters.applyFilter(path, i, hoverFilter.type);
} else {
filters.getDefaultFilter(path, i);
}
}
}
if (typeof w.config.chart.events.dataPointSelection === "function") {
w.config.chart.events.dataPointSelection(e, this.ctx, {
selectedDataPoints: w.interact.selectedDataPoints,
seriesIndex: i,
dataPointIndex: j,
w
});
}
if (e) {
Graphics._fireEvent(w, "dataPointSelection", [
e,
this.ctx,
{
selectedDataPoints: w.interact.selectedDataPoints,
seriesIndex: i,
dataPointIndex: j,
w
}
]);
}
}
/**
* @param {any} el
* @returns {{ x: number, y: number }}
*/
rotateAroundCenter(el) {
let coord = (
/** @type {any} */
{}
);
if (el && typeof el.getBBox === "function") {
coord = el.getBBox();
}
const x = coord.x + coord.width / 2;
const y = coord.y + coord.height / 2;
return {
x,
y
};
}
/**
* Sets up event delegation on a parent group element.
* Uses mouseover/mouseout (which bubble) to simulate mouseenter/mouseleave
* on matching child elements, reducing per-element listener overhead.
* @param {any} parentGroup
* @param {string} targetSelector
*/
setupEventDelegation(parentGroup, targetSelector) {
let currentHovered = null;
parentGroup.node.addEventListener("mouseover", (e) => {
const targetNode = Graphics._findDelegateTarget(
e.target,
parentGroup.node,
targetSelector
);
if (!targetNode || targetNode === currentHovered) return;
if (currentHovered && /** @type {any} */
currentHovered.instance) {
this.pathMouseLeave(
/** @type {any} */
currentHovered.instance,
e
);
}
currentHovered = targetNode;
if (targetNode.instance) {
this.pathMouseEnter(targetNode.instance, e);
}
});
parentGroup.node.addEventListener("mouseout", (e) => {
if (!currentHovered) return;
const relatedNode = e.relatedTarget ? Graphics._findDelegateTarget(
e.relatedTarget,
parentGroup.node,
targetSelector
) : null;
if (relatedNode !== currentHovered) {
if (currentHovered && /** @type {any} */
currentHovered.instance) {
this.pathMouseLeave(
/** @type {any} */
currentHovered.instance,
e
);
}
currentHovered = null;
}
});
parentGroup.node.addEventListener("mousedown", (e) => {
const targetNode = Graphics._findDelegateTarget(
e.target,
parentGroup.node,
targetSelector
);
if (targetNode && targetNode.instance) {
this.pathMouseDown(targetNode.instance, e);
}
});
}
// Fire a named event from w.globals.events without requiring a ctx reference.
// Mirrors Events.fireEvent() but reads the registry directly from w so that
// pathMouseEnter/Leave/Down work even when this.ctx is null (Graphics instances
// created without a ctx arg for drawing-only use cases).
/**
* @param {import('../types/internal').ChartStateW} w
* @param {string} name
* @param {any[]} args
*/
static _fireEvent(w, name2, args) {
const evs = w.globals.events;
if (!evs || !Object.prototype.hasOwnProperty.call(evs, name2)) return;
const handlers = (
/** @type {Record<string,any>} */
evs[name2]
);
for (let i = 0; i < handlers.length; i++) {
handlers[i].apply(null, args);
}
}
/**
* @param {any} node
* @param {Record<string, any>} boundary
* @param {string} selector
*/
static _findDelegateTarget(node, boundary, selector) {
while (node && node !== boundary && node !== document) {
if (node.matches && node.matches(selector)) return node;
node = node.parentNode;
}
return null;
}
/**
* @param {any} el
* @param {Record<string, any>} attrs
*/
static setAttrs(el, attrs) {
for (const key in attrs) {
if (Object.prototype.hasOwnProperty.call(attrs, key)) {
el.setAttribute(key, attrs[key]);
}
}
}
/**
* @param {string} text
* @param {string} fontSize
* @param {string | null | undefined} [fontFamily]
* @param {string} [transform]
* @returns {{ width: number, height: number }}
*/
getTextRects(text, fontSize, fontFamily, transform, useBBox = true) {
const w = this.w;
const cacheKey = [text, fontSize, fontFamily, transform, useBBox].join("\0");
const cache = w.globals.textRectsCache;
if (cache && cache.has(cacheKey)) {
return (
/** @type {{ width: number, height: number }} */
cache.get(cacheKey)
);
}
const virtualText = this.drawText({
x: -200,
y: -200,
text,
textAnchor: "start",
fontSize,
fontFamily,
foreColor: "#fff",
opacity: 0
});
if (transform) {
virtualText.attr("transform", transform);
}
w.dom.Paper.add(virtualText);
let rect = virtualText.bbox();
if (!useBBox) {
rect = virtualText.node.getBoundingClientRect();
}
virtualText.remove();
const result = {
width: rect.width,
height: rect.height
};
if (cache) {
cache.set(cacheKey, result);
}
return result;
}
/**
* append ... to long text
* http://stackoverflow.com/questions/9241315/trimming-text-to-a-given-pixel-width-in-svg
* @memberof Graphics
* @param {Record<string, any>} textObj
* @param {string} textString
* @param {number} width
**/
placeTextWithEllipsis(textObj, textString, width) {
if (typeof textObj.getComputedTextLength !== "function") return;
textObj.textContent = textString;
if (textString.length > 0) {
if (textObj.getComputedTextLength() >= width / 1.1) {
for (let x = textString.length - 3; x > 0; x -= 3) {
if (textObj.getSubStringLength(0, x) <= width / 1.1) {
textObj.textContent = textString.substring(0, x) + "...";
return;
}
}
textObj.textContent = ".";
}
}
}
}
const SVGNS = "http://www.w3.org/2000/svg";
class Point {
/**
* @param {number|{x:number,y:number}} x
* @param {number} [y]
*/
constructor(x, y) {
if (typeof x === "object") {
this.x = x.x;
this.y = x.y;
} else {
this.x = x || 0;
this.y = y || 0;
}
}
/**
* @param {Matrix} matrix
*/
transform(matrix) {
return matrix.apply(this);
}
clone() {
return new Point(this.x, this.y);
}
}
class Matrix {
/**
* @param {number} a
* @param {number} b
* @param {number} c
* @param {number} d
* @param {number} e
* @param {number} f
*/
constructor(a, b, c, d, e, f) {
this.a = a != null ? a : 1;
this.b = b != null ? b : 0;
this.c = c != null ? c : 0;
this.d = d != null ? d : 1;
this.e = e != null ? e : 0;
this.f = f != null ? f : 0;
}
/**
* @param {number} deg
*/
rotate(deg) {
const rad = deg * Math.PI / 180;
const cos = Math.cos(rad);
const sin = Math.sin(rad);
return this.multiply(new Matrix(cos, sin, -sin, cos, 0, 0));
}
/**
* @param {number} sx
* @param {number} sy
*/
scale(sx, sy) {
return this.multiply(new Matrix(sx, 0, 0, sy != null ? sy : sx, 0, 0));
}
/**
* @param {Matrix} m
*/
multiply(m) {
return new Matrix(
this.a * m.a + this.c * m.b,
this.b * m.a + this.d * m.b,
this.a * m.c + this.c * m.d,
this.b * m.c + this.d * m.d,
this.a * m.e + this.c * m.f + this.e,
this.b * m.e + this.d * m.f + this.f
);
}
/**
* @param {Point} point
*/
apply(point) {
return new Point(
this.a * point.x + this.c * point.y + this.e,
this.b * point.x + this.d * point.y + this.f
);
}
}
class Box {
/**
* @param {number} x
* @param {number} y
* @param {number} w
* @param {number} h
*/
constructor(x, y, w, h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.width = w;
this.height = h;
this.x2 = x + w;
this.y2 = y + h;
}
}
class Fill {
/**
* @param {import('../types/internal').ChartStateW} w
*/
constructor(w) {
this.w = w;
this.opts = null;
this.seriesIndex = 0;
this.patternIDs = [];
}
/**
* @param {Record<string, any>} params
*/
clippedImgArea(params) {
const w = this.w;
const cnf = w.config;
const svgW = parseInt(String(w.layout.gridWidth), 10);
const svgH = parseInt(String(w.layout.gridHeight), 10);
const size = svgW > svgH ? svgW : svgH;
const fillImg = params.image;
let imgWidth = 0;
let imgHeight = 0;
if (typeof params.width === "undefined" && typeof params.height === "undefined") {
if (cnf.fill.image.width !== void 0 && cnf.fill.image.height !== void 0) {
imgWidth = cnf.fill.image.width + 1;
imgHeight = cnf.fill.image.height;
} else {
imgWidth = size + 1;
imgHeight = size;
}
} else {
imgWidth = params.width;
imgHeight = params.height;
}
const elPattern = BrowserAPIs.createElementNS(SVGNS, "pattern");
Graphics.setAttrs(elPattern, {
id: params.patternID,
patternUnits: params.patternUnits ? params.patternUnits : "userSpaceOnUse",
width: imgWidth + "px",
height: imgHeight + "px"
});
const elImage = BrowserAPIs.createElementNS(SVGNS, "image");
elPattern.appendChild(elImage);
const SVGLib = Environment.isBrowser() ? (
/** @type {any} */
window.SVG
) : (
/** @type {any} */
global.SVG
);
elImage.setAttributeNS(SVGLib.xlink, "href", fillImg);
Graphics.setAttrs(elImage, {
x: 0,
y: 0,
preserveAspectRatio: "none",
width: imgWidth + "px",
height: imgHeight + "px"
});
elImage.style.opacity = params.opacity;
w.dom.elDefs.node.appendChild(elPattern);
}
/**
* @param {Record<string, any>} opts
*/
getSeriesIndex(opts) {
const w = this.w;
const cType = w.config.chart.type;
if ((cType === "bar" || cType === "rangeBar") && w.config.plotOptions.bar.distributed || cType === "heatmap" || cType === "treemap") {
this.seriesIndex = opts.seriesNumber;
} else {
this.seriesIndex = opts.seriesNumber % w.seriesData.series.length;
}
return this.seriesIndex;
}
/**
* @param {number[]} data
* @param {Record<string, any>} multiColorConfig
*/
computeColorStops(data, multiColorConfig) {
const w = this.w;
let maxPositive = null;
let minNegative = null;
for (const value of data) {
if (value >= multiColorConfig.threshold) {
if (maxPositive === null || value > maxPositive) {
maxPositive = value;
}
} else {
if (minNegative === null || value < minNegative) {
minNegative = value;
}
}
}
if (maxPositive === null) {
maxPositive = multiColorConfig.threshold;
}
if (minNegative === null) {
minNegative = multiColorConfig.threshold;
}
let totalRange = maxPositive - multiColorConfig.threshold + (multiColorConfig.threshold - minNegative);
if (totalRange === 0) {
totalRange = 1;
}
const negativePercentage = (multiColorConfig.threshold - minNegative) / totalRange * 100;
let offset = 100 - negativePercentage;
offset = Math.max(0, Math.min(offset, 100));
return [
{
offset,
color: multiColorConfig.colorAboveThreshold,
opacity: w.config.fill.opacity
},
{
offset: 0,
color: multiColorConfig.colorBelowThreshold,
opacity: w.config.fill.opacity
}
];
}
/**
* @param {Record<string, any>} opts
*/
fillPath(opts) {
var _a, _b, _c, _d;
const w = this.w;
this.opts = opts;
const cnf = this.w.config;
let pathFill;
let patternFill, gradientFill;
this.seriesIndex = this.getSeriesIndex(opts);
const drawMultiColorLine = cnf.plotOptions.line.colors.colorAboveThreshold && cnf.plotOptions.line.colors.colorBelowThreshold;
const fillColors = this.getFillColors();
let fillColor = fillColors[this.seriesIndex];
if (w.seriesData.seriesColors[this.seriesIndex] !== void 0) {
fillColor = w.seriesData.seriesColors[this.seriesIndex];
}
if (typeof fillColor === "function") {
fillColor = fillColor({
seriesIndex: this.seriesIndex,
dataPointIndex: opts.dataPointIndex,
value: opts.value,
w
});
}
const fillType = opts.fillType ? opts.fillType : this.getFillType(this.seriesIndex);
let fillOpacity = Array.isArray(cnf.fill.opacity) ? cnf.fill.opacity[this.seriesIndex] : cnf.fill.opacity;
const useGradient = fillType === "gradient" || drawMultiColorLine;
if (opts.color) {
fillColor = opts.color;
}
const seriesItem = (
/** @type {Record<string,any>} */
w.config.series[this.seriesIndex]
);
if ((_b = (_a = seriesItem == null ? void 0 : seriesItem.data) == null ? void 0 : _a[opts.dataPointIndex]) == null ? void 0 : _b.fillColor) {
fillColor = (_d = (_c = seriesItem == null ? void 0 : seriesItem.data) == null ? void 0 : _c[opts.dataPointIndex]) == null ? void 0 : _d.fillColor;
}
if (!fillColor) {
fillColor = "#fff";
console.warn("undefined color - ApexCharts");
}
if (opts.opacity !== void 0 && opts.opacity !== null) fillOpacity = opts.opacity;
let defaultColor = fillColor;
if (Utils$1.isCSSVariable(fillColor)) {
defaultColor = Utils$1.applyOpacityToColor(fillColor, fillOpacity);
} else if (fillColor.indexOf("rgb") === -1) {
if (fillColor.indexOf("#") === -1) {
defaultColor = fillColor;
} else if (fillColor.length < 9) {
defaultColor = Utils$1.hexToRgba(fillColor, fillOpacity);
}
} else {
if (fillColor.indexOf("rgba") > -1) {
fillOpacity = Utils$1.getOpacityFromRGBA(fillColor);
} else {
defaultColor = Utils$1.hexToRgba(Utils$1.rgb2hex(fillColor), fillOpacity);
}
}
const resolvedFillColor = Utils$1.isCSSVariable(fillColor) ? Utils$1.getThemeColor(fillColor) : fillColor;
if (fillType === "pattern") {
patternFill = this.handlePatternFill({
fillConfig: opts.fillConfig,
patternFill,
fillColor: resolvedFillColor,
defaultColor
});
}
if (useGradient) {
const colorStops = cnf.fill.gradient.colorStops ? [...cnf.fill.gradient.colorStops] : [];
let type = cnf.fill.gradient.type;
if (drawMultiColorLine) {
colorStops[this.seriesIndex] = this.computeColorStops(
w.seriesData.series[this.seriesIndex],
cnf.plotOptions.line.colors
);
type = "vertical";
}
gradientFill = this.handleGradientFill({
type,
fillConfig: opts.fillConfig,
fillColor: resolvedFillColor,
fillOpacity,
colorStops,
i: this.seriesIndex
});
}
if (fillType === "image") {
const imgSrc = cnf.fill.image.src;
const patternID = opts.patternID ? opts.patternID : "";
const patternKey = `pattern${w.globals.cuid}${opts.seriesNumber + 1}${patternID}`;
if (this.patternIDs.indexOf(patternKey) === -1) {
this.clippedImgArea({
opacity: fillOpacity,
image: Array.isArray(imgSrc) ? opts.seriesNumber < imgSrc.length ? imgSrc[opts.seriesNumber] : imgSrc[0] : imgSrc,
width: opts.width ? opts.width : void 0,
height: opts.height ? opts.height : void 0,
patternUnits: opts.patternUnits,
patternID: patternKey
});
this.patternIDs.push(patternKey);
}
pathFill = `url(#${patternKey})`;
} else if (useGradient) {
pathFill = gradientFill;
} else if (fillType === "pattern") {
pathFill = patternFill;
} else {
pathFill = defaultColor;
}
if (opts.solid) {
pathFill = defaultColor;
}
return pathFill;
}
/**
* @param {number} seriesIndex
*/
getFillType(seriesIndex) {
const w = this.w;
if (Array.isArray(w.config.fill.type)) {
return w.config.fill.type[seriesIndex];
} else {
return w.config.fill.type;
}
}
getFillColors() {
const w = this.w;
const cnf = w.config;
const opts = this.opts;
let fillColors = [];
if (w.globals.comboCharts) {
if (
/** @type {Record<string,any>} */
w.config.series[this.seriesIndex].type === "line"
) {
if (Array.isArray(w.globals.stroke.colors)) {
fillColors = w.globals.stroke.colors;
} else {
fillColors.push(w.globals.stroke.colors);
}
} else {
if (Array.isArray(w.globals.fill.colors)) {
fillColors = w.globals.fill.colors;
} else {
fillColors.push(w.globals.fill.colors);
}
}
} else {
if (cnf.chart.type === "line") {
if (Array.isArray(w.globals.stroke.colors)) {
fillColors = w.globals.stroke.colors;
} else {
fillColors.push(w.globals.stroke.colors);
}
} else {
if (Array.isArray(w.globals.fill.colors)) {
fillColors = w.globals.fill.colors;
} else {
fillColors.push(w.globals.fill.colors);
}
}
}
if (typeof opts.fillColors !== "undefined") {
fillColors = [];
if (Array.isArray(opts.fillColors)) {
fillColors = opts.fillColors.slice();
} else {
fillColors.push(opts.fillColors);
}
}
return fillColors;
}
/** @param {{fillConfig: any, patternFill: any, fillColor: any, defaultColor: any}} opts */
handlePatternFill({ fillConfig, patternFill, fillColor, defaultColor }) {
let fillCnf = this.w.config.fill;
if (fillConfig) {
fillCnf = fillConfig;
}
const opts = this.opts;
const graphics = new Graphics(this.w);
const patternStrokeWidth = Array.isArray(fillCnf.pattern.strokeWidth) ? fillCnf.pattern.strokeWidth[this.seriesIndex] : fillCnf.pattern.strokeWidth;
const patternLineColor = fillColor;
if (Array.isArray(fillCnf.pattern.style)) {
if (typeof fillCnf.pattern.style[opts.seriesNumber] !== "undefined") {
const pf = graphics.drawPattern(
fillCnf.pattern.style[opts.seriesNumber],
fillCnf.pattern.width,
fillCnf.pattern.height,
patternLineColor,
patternStrokeWidth
);
patternFill = pf;
} else {
patternFill = defaultColor;
}
} else {
patternFill = graphics.drawPattern(
fillCnf.pattern.style,
fillCnf.pattern.width,
fillCnf.pattern.height,
patternLineColor,
patternStrokeWidth
);
}
return patternFill;
}
handleGradientFill({
type,
fillColor,
fillOpacity,
fillConfig,
colorStops,
i
}) {
let fillCnf = this.w.config.fill;
if (fillConfig) {
fillCnf = __spreadValues(__spreadValues({}, fillCnf), fillConfig);
}
const opts = this.opts;
const graphics = new Graphics(this.w);
const utils = new Utils$1();
type = type || fillCnf.gradient.type;
let gradientFrom = fillColor;
let gradientTo;
let opacityFrom = fillCnf.gradient.opacityFrom === void 0 ? fillOpacity : Array.isArray(fillCnf.gradient.opacityFrom) ? fillCnf.gradient.opacityFrom[i] : fillCnf.gradient.opacityFrom;
if (gradientFrom.indexOf("rgba") > -1) {
opacityFrom = Utils$1.getOpacityFromRGBA(gradientFrom);
}
let opacityTo = fillCnf.gradient.opacityTo === void 0 ? fillOpacity : Array.isArray(fillCnf.gradient.opacityTo) ? fillCnf.gradient.opacityTo[i] : fillCnf.gradient.opacityTo;
if (fillCnf.gradient.gradientToColors === void 0 || fillCnf.gradient.gradientToColors.length === 0) {
if (fillCnf.gradient.shade === "dark") {
gradientTo = utils.shadeColor(
parseFloat(fillCnf.gradient.shadeIntensity) * -1,
fillColor.indexOf("rgb") > -1 ? Utils$1.rgb2hex(fillColor) : fillColor
);
} else {
gradientTo = utils.shadeColor(
parseFloat(fillCnf.gradient.shadeIntensity),
fillColor.indexOf("rgb") > -1 ? Utils$1.rgb2hex(fillColor) : fillColor
);
}
} else {
if (fillCnf.gradient.gradientToColors[opts.seriesNumber]) {
const gToColor = fillCnf.gradient.gradientToColors[opts.seriesNumber];
gradientTo = gToColor;
if (gToColor.indexOf("rgba") > -1) {
opacityTo = Utils$1.getOpacityFromRGBA(gToColor);
}
} else {
gradientTo = fillColor;
}
}
if (fillCnf.gradient.gradientFrom) {
gradientFrom = fillCnf.gradient.gradientFrom;
}
if (fillCnf.gradient.gradientTo) {
gradientTo = fillCnf.gradient.gradientTo;
}
if (fillCnf.gradient.inverseColors) {
const t = gradientFrom;
gradientFrom = gradientTo;
gradientTo = t;
}
if (gradientFrom.indexOf("rgb") > -1) {
gradientFrom = Utils$1.rgb2hex(gradientFrom);
}
if (gradientTo.indexOf("rgb") > -1) {
gradientTo = Utils$1.rgb2hex(gradientTo);
}
return graphics.drawGradient(
type,
gradientFrom,
gradientTo,
opacityFrom,
opacityTo,
opts.size,
fillCnf.gradient.stops,
colorStops,
i
);
}
}
class Markers {
/**
* @param {import('../types/internal').ChartStateW} w
* @param {import('../types/internal').ChartContext} ctx
*/
constructor(w, ctx) {
this.w = w;
this.ctx = ctx;
this._filters = new Filters(this.w);
this._graphics = new Graphics(this.w, this.ctx);
}
setGlobalMarkerSize() {
const w = this.w;
w.globals.markers.size = Array.isArray(w.config.markers.size) ? w.config.markers.size : [w.config.markers.size];
if (w.globals.markers.size.length > 0) {
if (w.globals.markers.size.length < w.seriesData.series.length + 1) {
for (let i = 0; i <= w.seriesData.series.length; i++) {
if (typeof w.globals.markers.size[i] === "undefined") {
w.globals.markers.size.push(w.globals.markers.size[0]);
}
}
}
} else {
w.globals.markers.size = w.config.series.map(
() => (
/** @type {number} */
w.config.markers.size
)
);
}
}
/** @param {{ pointsPos?: any, seriesIndex?: any, j?: any, pSize?: any, alwaysDrawMarker?: boolean, isVirtualPoint?: boolean }} opts */
plotChartMarkers({
pointsPos,
seriesIndex,
j,
pSize,
alwaysDrawMarker = false,
isVirtualPoint = false
}) {
const w = this.w;
const i = seriesIndex;
const p = pointsPos;
let elMarkersWrap = null;
const graphics = new Graphics(this.w);
const hasDiscreteMarkers = w.config.markers.discrete && w.config.markers.discrete.length;
if (Array.isArray(p.x)) {
for (let q = 0; q < p.x.length; q++) {
let markerElement;
let dataPointIndex = j;
let invalidMarker = !Utils$1.isNumber(p.y[q]);
if (w.globals.markers.largestSize === 0 && w.globals.hasNullValues && w.seriesData.series[i][j + 1] !== null && !isVirtualPoint) {
invalidMarker = true;
}
if (j === 1 && q === 0) dataPointIndex = 0;
if (j === 1 && q === 1) dataPointIndex = 1;
let markerClasses = "apexcharts-marker";
if ((w.config.chart.type === "line" || w.config.chart.type === "area") && !w.globals.comboCharts && !w.config.tooltip.intersect) {
markerClasses += " no-pointer-events";
}
const shouldMarkerDraw = Array.isArray(w.config.markers.size) ? w.globals.markers.size[seriesIndex] > 0 : w.config.markers.size > 0;
if (shouldMarkerDraw || alwaysDrawMarker || hasDiscreteMarkers) {
if (!invalidMarker) {
markerClasses += ` w${Utils$1.randomId()}`;
}
const opts = this.getMarkerConfig({
cssClass: markerClasses,
seriesIndex,
dataPointIndex
});
const _si = (
/** @type {Record<string,any>} */
w.config.series[i]
);
if (_si.data[dataPointIndex]) {
if (_si.data[dataPointIndex].fillColor) {
opts.pointFillColor = _si.data[dataPointIndex].fillColor;
}
if (_si.data[dataPointIndex].strokeColor) {
opts.pointStrokeColor = _si.data[dataPointIndex].strokeColor;
}
}
if (typeof pSize !== "undefined") {
opts.pSize = pSize;
}
if (p.x[q] < -w.globals.markers.largestSize || p.x[q] > w.layout.gridWidth + w.globals.markers.largestSize || p.y[q] < -w.globals.markers.largestSize || p.y[q] > w.layout.gridHeight + w.globals.markers.largestSize) {
opts.pSize = 0;
}
if (!invalidMarker) {
const shouldCreateMarkerWrap = w.globals.markers.size[seriesIndex] > 0 || alwaysDrawMarker || hasDiscreteMarkers;
if (shouldCreateMarkerWrap && !elMarkersWrap) {
elMarkersWrap = graphics.group({
class: alwaysDrawMarker || hasDiscreteMarkers ? "" : "apexcharts-series-markers"
});
elMarkersWrap.attr(
"clip-path",
`url(#gridRectMarkerMask${w.globals.cuid})`
);
this.setupMarkerDelegation(elMarkersWrap);
}
markerElement = graphics.drawMarker(p.x[q], p.y[q], opts);
markerElement.attr("rel", dataPointIndex);
markerElement.attr("j", dataPointIndex);
markerElement.attr("index", seriesIndex);
markerElement.node.setAttribute("default-marker-size", opts.pSize);
this._filters.setSelectionFilter(
markerElement,
seriesIndex,
dataPointIndex
);
if (elMarkersWrap) {
elMarkersWrap.add(markerElement);
}
}
} else {
if (typeof w.globals.pointsArray[seriesIndex] === "undefined")
w.globals.pointsArray[seriesIndex] = [];
w.globals.pointsArray[seriesIndex].push([p.x[q], p.y[q]]);
}
}
}
return elMarkersWrap;
}
/** @param {{cssClass: any, seriesIndex: any, dataPointIndex?: any, radius?: any, size?: any, strokeWidth?: any}} opts */
getMarkerConfig({
cssClass,
seriesIndex,
dataPointIndex = null,
radius = null,
size = null,
strokeWidth = null
}) {
const w = this.w;
const pStyle = this.getMarkerStyle(seriesIndex);
let pSize = size === null ? w.globals.markers.size[seriesIndex] : size;
const m = w.config.markers;
if (dataPointIndex !== null && m.discrete.length) {
m.discrete.map((marker) => {
if (marker.seriesIndex === seriesIndex && marker.dataPointIndex === dataPointIndex) {
pStyle.pointStrokeColor = marker.strokeColor;
pStyle.pointFillColor = marker.fillColor;
pSize = marker.size;
pStyle.pointShape = marker.shape;
}
});
}
return {
pSize: radius === null ? pSize : radius,
pRadius: radius !== null ? radius : m.radius,
pointStrokeWidth: strokeWidth !== null ? strokeWidth : Array.isArray(m.strokeWidth) ? m.strokeWidth[seriesIndex] : m.strokeWidth,
pointStrokeColor: pStyle.pointStrokeColor,
pointFillColor: pStyle.pointFillColor,
shape: pStyle.pointShape || (Array.isArray(m.shape) ? m.shape[seriesIndex] : m.shape),
class: cssClass,
pointStrokeOpacity: Array.isArray(m.strokeOpacity) ? m.strokeOpacity[seriesIndex] : m.strokeOpacity,
pointStrokeDashArray: Array.isArray(m.strokeDashArray) ? m.strokeDashArray[seriesIndex] : m.strokeDashArray,
pointFillOpacity: Array.isArray(m.fillOpacity) ? m.fillOpacity[seriesIndex] : m.fillOpacity,
seriesIndex
};
}
/**
* @param {any} parentGroup
*/
setupMarkerDelegation(parentGroup) {
const w = this.w;
const selector = ".apexcharts-marker";
this._graphics.setupEventDelegation(parentGroup, selector);
parentGroup.node.addEventListener("click", (e) => {
if (w.config.markers.onClick) {
const targetNode = Graphics._findDelegateTarget(
e.target,
parentGroup.node,
selector
);
if (targetNode) w.config.markers.onClick(e);
}
});
parentGroup.node.addEventListener("dblclick", (e) => {
if (w.config.markers.onDblClick) {
const targetNode = Graphics._findDelegateTarget(
e.target,
parentGroup.node,
selector
);
if (targetNode) w.config.markers.onDblClick(e);
}
});
parentGroup.node.addEventListener(
"touchstart",
(e) => {
const targetNode = Graphics._findDelegateTarget(
e.target,
parentGroup.node,
selector
);
if (targetNode && targetNode.instance) {
this._graphics.pathMouseDown(targetNode.instance, e);
}
},
{ passive: true }
);
}
/**
* @param {any} marker
*/
addEvents(marker) {
const w = this.w;
marker.node.addEventListener(
"mouseenter",
this._graphics.pathMouseEnter.bind(this.ctx, marker)
);
marker.node.addEventListener(
"mouseleave",
this._graphics.pathMouseLeave.bind(this.ctx, marker)
);
marker.node.addEventListener(
"mousedown",
this._graphics.pathMouseDown.bind(this.ctx, marker)
);
marker.node.addEventListener("click", w.config.markers.onClick);
marker.node.addEventListener("dblclick", w.config.markers.onDblClick);
marker.node.addEventListener(
"touchstart",
this._graphics.pathMouseDown.bind(this.ctx, marker),
{ passive: true }
);
}
/**
* @returns {any}
* @param {number} seriesIndex
*/
getMarkerStyle(seriesIndex) {
const w = this.w;
const colors = w.globals.markers.colors;
const strokeColors = w.config.markers.strokeColor || w.config.markers.strokeColors;
const pointStrokeColor = Array.isArray(strokeColors) ? strokeColors[seriesIndex] : strokeColors;
const pointFillColor = Array.isArray(colors) ? colors[seriesIndex] : colors;
return {
pointStrokeColor,
pointFillColor
};
}
}
class Scatter {
/**
* @param {import('../types/internal').ChartStateW} w
* @param {import('../types/internal').ChartContext} ctx
*/
constructor(w, ctx) {
this.ctx = ctx;
this.w = w;
this.initialAnim = this.w.config.chart.animations.enabled;
this.anim = new Animations(this.w);
this.filters = new Filters(this.w);
this.fill = new Fill(this.w);
this.markers = new Markers(this.w, this.ctx);
this.graphics = new Graphics(this.w);
}
/**
* @param {Element} elSeries
* @param {number} j
* @param {Record<string, any>} opts
*/
draw(elSeries, j, opts) {
const w = this.w;
const graphics = this.graphics;
const realIndex = opts.realIndex;
const pointsPos = opts.pointsPos;
const zRatio = opts.zRatio;
const elPointsMain = opts.elParent;
const elPointsWrap = graphics.group({
class: `apexcharts-series-markers apexcharts-series-${w.config.chart.type}`
});
elPointsWrap.attr("clip-path", `url(#gridRectMarkerMask${w.globals.cuid})`);
this.markers.setupMarkerDelegation(elPointsWrap);
if (Array.isArray(pointsPos.x)) {
for (let q = 0; q < pointsPos.x.length; q++) {
let dataPointIndex = j + 1;
let shouldDraw = true;
if (j === 0 && q === 0) dataPointIndex = 0;
if (j === 0 && q === 1) dataPointIndex = 1;
let radius = w.globals.markers.size[realIndex];
if (zRatio !== Infinity) {
const bubble = w.config.plotOptions.bubble;
radius = w.seriesData.seriesZ[realIndex][dataPointIndex];
if (bubble.zScaling) {
radius /= zRatio;
}
if (bubble.minBubbleRadius && radius < bubble.minBubbleRadius) {
radius = bubble.minBubbleRadius;
}
if (bubble.maxBubbleRadius && radius > bubble.maxBubbleRadius) {
radius = bubble.maxBubbleRadius;
}
}
const x = pointsPos.x[q];
const y = pointsPos.y[q];
radius = radius || 0;
if (y === null || typeof w.seriesData.series[realIndex][dataPointIndex] === "undefined") {
shouldDraw = false;
}
if (shouldDraw) {
const point = this.drawPoint(
x,
y,
radius,
realIndex,
dataPointIndex,
j
);
elPointsWrap.add(point);
}
elPointsMain.add(elPointsWrap);
}
}
}
/**
* @param {number} x
* @param {number} y
* @param {number} radius
* @param {number} realIndex
* @param {number} dataPointIndex
* @param {number} j
*/
drawPoint(x, y, radius, realIndex, dataPointIndex, j) {
const w = this.w;
const i = realIndex;
const anim = this.anim;
const filters = this.filters;
const fill = this.fill;
const markers = this.markers;
const graphics = this.graphics;
const markerConfig = markers.getMarkerConfig({
cssClass: "apexcharts-marker",
seriesIndex: i,
dataPointIndex,
radius: w.config.chart.type === "bubble" || w.globals.comboCharts && w.config.series[realIndex] && /** @type {Record<string,any>} */
w.config.series[realIndex].type === "bubble" ? radius : null
});
let pathFillCircle = fill.fillPath({
seriesNumber: realIndex,
dataPointIndex,
color: markerConfig.pointFillColor,
patternUnits: "objectBoundingBox",
value: w.seriesData.series[realIndex][j]
});
const el = graphics.drawMarker(x, y, markerConfig);
const _si = (
/** @type {Record<string,any>} */
w.config.series[i]
);
if (_si.data[dataPointIndex]) {
if (_si.data[dataPointIndex].fillColor) {
pathFillCircle = _si.data[dataPointIndex].fillColor;
}
}
el.attr({
fill: pathFillCircle
});
if (w.config.chart.dropShadow.enabled) {
const dropShadow = w.config.chart.dropShadow;
filters.dropShadow(el, dropShadow, realIndex);
}
if (this.initialAnim && !w.globals.dataChanged && !w.globals.resized) {
const speed = w.config.chart.animations.speed;
anim.animateMarker(
el,
speed,
/** @type {any} */
w.globals.easing,
() => {
window.setTimeout(() => {
anim.animationCompleted(el);
}, 100);
}
);
} else {
w.globals.animationEnded = true;
}
el.attr({
rel: dataPointIndex,
j: dataPointIndex,
index: realIndex,
"default-marker-size": markerConfig.pSize
});
filters.setSelectionFilter(el, realIndex, dataPointIndex);
el.node.classList.add("apexcharts-marker");
return el;
}
/**
* @param {number} y
*/
centerTextInBubble(y) {
const w = this.w;
y = y + parseInt(w.config.dataLabels.style.fontSize, 10) / 4;
return {
y
};
}
}
class DataLabels {
/**
* @param {import('../types/internal').ChartStateW} w
* @param {import('../types/internal').ChartContext | null} ctx
*/
constructor(w, ctx = null) {
this.w = w;
this.ctx = ctx;
}
// When there are many datalabels to be printed, and some of them overlaps each other in the same series, this method will take care of that
// Also, when datalabels exceeds the drawable area and get clipped off, we need to adjust and move some pixels to make them visible again
/**
* @param {number} x
* @param {number} y
* @param {any} val
* @param {number} i
* @param {number} dataPointIndex
* @param {boolean} alwaysDrawDataLabel
* @param {string} fontSize
*/
dataLabelsCorrection(x, y, val, i, dataPointIndex, alwaysDrawDataLabel, fontSize) {
const w = this.w;
const graphics = new Graphics(this.w);
let drawnextLabel = false;
const textRects = (
/** @type {any} */
graphics.getTextRects(val, fontSize)
);
const width = textRects.width;
const height = textRects.height;
if (y < 0) y = 0;
if (y > w.layout.gridHeight + height) y = w.layout.gridHeight + height / 2;
if (typeof /** @type {any} */
w.globals.dataLabelsRects[i] === "undefined") {
w.globals.dataLabelsRects[i] = [];
}
w.globals.dataLabelsRects[i].push({
x,
y,
width,
height
});
const len = (
/** @type {any} */
w.globals.dataLabelsRects[i].length - 2
);
const lastDrawnIndex = typeof w.globals.lastDrawnDataLabelsIndexes[i] !== "undefined" ? w.globals.lastDrawnDataLabelsIndexes[i][w.globals.lastDrawnDataLabelsIndexes[i].length - 1] : 0;
if (typeof /** @type {any} */
w.globals.dataLabelsRects[i][len] !== "undefined") {
const lastDataLabelRect = (
/** @type {any} */
w.globals.dataLabelsRects[i][lastDrawnIndex]
);
if (
// next label forward and x not intersecting
x > lastDataLabelRect.x + lastDataLabelRect.width || y > lastDataLabelRect.y + lastDataLabelRect.height || y + height < lastDataLabelRect.y || x + width < lastDataLabelRect.x
) {
drawnextLabel = true;
}
}
if (dataPointIndex === 0 || alwaysDrawDataLabel) {
drawnextLabel = true;
}
return {
x,
y,
textRects,
drawnextLabel
};
}
/** @param {{type: any, pos: any, i: any, j: any, isRangeStart: any, strokeWidth?: any}} opts */
drawDataLabel({ type, pos, i, j, isRangeStart, strokeWidth = 2 }) {
const w = this.w;
const graphics = new Graphics(this.w);
const dataLabelsConfig = w.config.dataLabels;
let x = 0;
let y = 0;
let dataPointIndex = j;
let elDataLabelsWrap = null;
const seriesCollapsed = w.globals.collapsedSeriesIndices.indexOf(i) !== -1;
if (seriesCollapsed || !dataLabelsConfig.enabled || !Array.isArray(pos.x)) {
return elDataLabelsWrap;
}
elDataLabelsWrap = graphics.group({
class: "apexcharts-data-labels"
});
for (let q = 0; q < pos.x.length; q++) {
x = pos.x[q] + dataLabelsConfig.offsetX;
y = pos.y[q] + dataLabelsConfig.offsetY + strokeWidth;
if (!isNaN(x)) {
if (j === 1 && q === 0) dataPointIndex = 0;
if (j === 1 && q === 1) dataPointIndex = 1;
let val = w.seriesData.series[i][dataPointIndex];
if (type === "rangeArea") {
if (isRangeStart) {
val = w.rangeData.seriesRangeStart[i][dataPointIndex];
} else {
val = w.rangeData.seriesRangeEnd[i][dataPointIndex];
}
}
let text = "";
const getText = (v) => {
return w.config.dataLabels.formatter(v, {
seriesIndex: i,
dataPointIndex,
w
});
};
if (w.config.chart.type === "bubble") {
val = w.seriesData.seriesZ[i][dataPointIndex];
text = getText(val);
y = pos.y[q];
const scatter = new Scatter(
this.w,
/** @type {import('../types/internal').ChartContext} */
this.ctx
);
const centerTextInBubbleCoords = scatter.centerTextInBubble(y);
y = centerTextInBubbleCoords.y;
} else {
if (typeof val !== "undefined") {
text = getText(val);
}
}
let textAnchor = w.config.dataLabels.textAnchor;
if (w.globals.isSlopeChart) {
if (dataPointIndex === 0) {
textAnchor = "end";
} else if (dataPointIndex === /** @type {Record<string,any>} */
w.config.series[i].data.length - 1) {
textAnchor = "start";
} else {
textAnchor = "middle";
}
}
this.plotDataLabelsText({
x,
y,
text,
i,
j: dataPointIndex,
parent: elDataLabelsWrap,
offsetCorrection: true,
dataLabelsConfig: w.config.dataLabels,
textAnchor
});
}
}
return elDataLabelsWrap;
}
/**
* @param {Record<string, any>} opts
*/
plotDataLabelsText(opts) {
const w = this.w;
const graphics = new Graphics(this.w);
let {
x,
y,
i,
j,
text,
textAnchor,
fontSize,
parent,
dataLabelsConfig,
color,
alwaysDrawDataLabel,
offsetCorrection,
className
} = opts;
let dataLabelText = null;
if (Array.isArray(w.config.dataLabels.enabledOnSeries)) {
if (w.config.dataLabels.enabledOnSeries.indexOf(i) < 0) {
return dataLabelText;
}
}
let correctedLabels = {
x,
y,
drawnextLabel: true,
textRects: null
};
if (offsetCorrection) {
correctedLabels = this.dataLabelsCorrection(
x,
y,
text,
i,
j,
alwaysDrawDataLabel,
parseInt(
/** @type {any} */
dataLabelsConfig.style.fontSize,
10
).toString()
);
}
if (!w.interact.zoomed) {
x = correctedLabels.x;
y = correctedLabels.y;
}
if (correctedLabels.textRects) {
const barPad = w.globals.barPadForNumericAxis || 0;
if (x < -(barPad + 20) - /** @type {any} */
correctedLabels.textRects.width || x > w.layout.gridWidth + /** @type {any} */
correctedLabels.textRects.width + barPad + 30) {
text = "";
}
}
let dataLabelColor = w.globals.dataLabels.style.colors[i];
if ((w.config.chart.type === "bar" || w.config.chart.type === "rangeBar") && w.config.plotOptions.bar.distributed || w.config.dataLabels.distributed) {
dataLabelColor = w.globals.dataLabels.style.colors[j];
}
if (typeof dataLabelColor === "function") {
dataLabelColor = /** @type {any} */
dataLabelColor({
series: w.seriesData.series,
seriesIndex: i,
dataPointIndex: j,
w
});
}
if (color) {
dataLabelColor = color;
}
let offX = dataLabelsConfig.offsetX;
let offY = dataLabelsConfig.offsetY;
if (w.config.chart.type === "bar" || w.config.chart.type === "rangeBar") {
offX = 0;
offY = 0;
}
if (w.globals.isSlopeChart) {
if (j !== 0) {
offX = dataLabelsConfig.offsetX * -2 + 5;
}
if (j !== 0 && j !== /** @type {Record<string,any>} */
w.config.series[i].data.length - 1) {
offX = 0;
}
}
if (correctedLabels.drawnextLabel) {
if (textAnchor === "middle") {
if (x === w.layout.gridWidth) {
textAnchor = "end";
}
}
dataLabelText = graphics.drawText({
x: x + offX,
y: y + offY,
foreColor: dataLabelColor,
textAnchor: textAnchor || dataLabelsConfig.textAnchor,
text,
fontSize: fontSize || dataLabelsConfig.style.fontSize,
fontFamily: dataLabelsConfig.style.fontFamily,
fontWeight: dataLabelsConfig.style.fontWeight || "normal"
});
dataLabelText.attr({
class: className || "apexcharts-datalabel",
cx: x,
cy: y
});
if (dataLabelsConfig.dropShadow.enabled) {
const textShadow = dataLabelsConfig.dropShadow;
const filters = new Filters(this.w);
filters.dropShadow(dataLabelText, textShadow);
}
parent.add(dataLabelText);
if (typeof w.globals.lastDrawnDataLabelsIndexes[i] === "undefined") {
w.globals.lastDrawnDataLabelsIndexes[i] = [];
}
w.globals.lastDrawnDataLabelsIndexes[i].push(j);
}
return dataLabelText;
}
/**
* @param {Element} el
* @param {{x: number, y: number, width: number, height: number}} coords
*/
addBackgroundToDataLabel(el, coords) {
const w = this.w;
const bCnf = w.config.dataLabels.background;
const paddingH = bCnf.padding;
const paddingV = bCnf.padding / 2;
const width = coords.width;
const height = coords.height;
const graphics = new Graphics(this.w);
const elRect = graphics.drawRect(
coords.x - paddingH,
coords.y - paddingV / 2,
width + paddingH * 2,
height + paddingV,
bCnf.borderRadius,
w.config.chart.background === "transparent" || !w.config.chart.background ? "#fff" : w.config.chart.background,
bCnf.opacity,
bCnf.borderWidth,
bCnf.borderColor
);
if (bCnf.dropShadow.enabled) {
const filters = new Filters(this.w);
filters.dropShadow(elRect, bCnf.dropShadow);
}
return elRect;
}
dataLabelsBackground() {
var _a;
const w = this.w;
if (w.config.chart.type === "bubble") return;
const elDataLabels = w.dom.baseEl.querySelectorAll(
".apexcharts-datalabels text"
);
for (let i = 0; i < elDataLabels.length; i++) {
const el = elDataLabels[i];
const coords = (
/** @type {SVGGraphicsElement} */
el.getBBox()
);
let elRect = null;
if (coords.width && coords.height) {
elRect = this.addBackgroundToDataLabel(el, coords);
}
if (elRect) {
(_a = el.parentNode) == null ? void 0 : _a.insertBefore(elRect.node, el);
const background = w.config.dataLabels.background.backgroundColor || el.getAttribute("fill");
const shouldAnim = w.config.chart.animations.enabled && !w.globals.resized && !w.globals.dataChanged;
if (shouldAnim) {
elRect.animate().attr({ fill: background });
} else {
elRect.attr({ fill: background });
}
el.setAttribute("fill", w.config.dataLabels.background.foreColor);
}
}
}
bringForward() {
const w = this.w;
const elDataLabelsNodes = w.dom.baseEl.querySelectorAll(
".apexcharts-datalabels"
);
const elSeries = w.dom.baseEl.querySelector(
".apexcharts-plot-series:last-child"
);
for (let i = 0; i < elDataLabelsNodes.length; i++) {
if (elSeries) {
elSeries.insertBefore(elDataLabelsNodes[i], elSeries.nextSibling);
}
}
}
}
class PerformanceCache {
/**
* Invalidate all caches
* @param {import('../types/internal').ChartStateW} w - ApexCharts state object
*/
static invalidateAll(w) {
if (!w || !w.globals) return;
if (w.globals.cachedSelectors) {
w.globals.cachedSelectors = {};
}
if (w.globals.domCache) {
w.globals.domCache.clear();
}
w.globals.dimensionCache = {};
}
/**
* Invalidate dimension cache only
* @param {import('../types/internal').ChartStateW} w - ApexCharts state object
*/
static invalidateDimensions(w) {
if (!w || !w.globals) return;
w.globals.dimensionCache = {};
}
/**
* Invalidate selector cache only
* @param {import('../types/internal').ChartStateW} w - ApexCharts state object
*/
static invalidateSelectors(w) {
if (!w || !w.globals) return;
if (w.globals.cachedSelectors) {
w.globals.cachedSelectors = {};
}
}
/**
* Get cached selector result or compute and cache it
* @param {import('../types/internal').ChartStateW} w - ApexCharts state object
* @param {string} key - Cache key
* @param {Function} queryFn - Function to execute if not cached
* @returns {*} Cached or newly computed result
*/
static getCachedSelector(w, key, queryFn) {
if (!w || !w.globals) return queryFn();
if (!w.globals.cachedSelectors) {
w.globals.cachedSelectors = {};
}
if (!w.globals.cachedSelectors[key]) {
w.globals.cachedSelectors[key] = queryFn();
}
return w.globals.cachedSelectors[key];
}
/**
* Get cached dimension or compute and cache it
* @param {import('../types/internal').ChartStateW} w - ApexCharts state object
* @param {string} key - Cache key
* @param {Function} computeFn - Function to compute dimensions
* @param {number} maxAge - Maximum cache age in milliseconds (default: 1000ms)
* @returns {*} Cached or newly computed dimensions
*/
static getCachedDimension(w, key, computeFn, maxAge = 1e3) {
if (!w || !w.globals) return computeFn();
if (!w.globals.dimensionCache) {
w.globals.dimensionCache = {};
}
const cache = w.globals.dimensionCache[key];
const now = Date.now();
if (cache && cache.lastUpdate && now - cache.lastUpdate < maxAge) {
return cache.value;
}
const value = computeFn();
w.globals.dimensionCache[key] = {
value,
lastUpdate: now
};
return value;
}
/**
* Cache a DOM element reference
* @param {import('../types/internal').ChartStateW} w - ApexCharts state object
* @param {string} key - Cache key
* @param {Element} element - DOM element to cache
*/
static cacheDOMElement(w, key, element) {
if (!w || !w.globals) return;
if (!w.globals.domCache) {
w.globals.domCache = /* @__PURE__ */ new Map();
}
w.globals.domCache.set(key, element);
}
/**
* Get cached DOM element
* @param {import('../types/internal').ChartStateW} w - ApexCharts state object
* @param {string} key - Cache key
* @returns {Element|null} Cached element or null
*/
static getCachedDOMElement(w, key) {
if (!w || !w.globals || !w.globals.domCache) return null;
return w.globals.domCache.get(key) || null;
}
}
class AxesUtils {
/**
* @param {import('../../types/internal').ChartStateW} w
*/
constructor(w, { theme = null, timeScale = null } = {}) {
this.w = w;
this.theme = theme;
this.timeScale = timeScale;
}
// Based on the formatter function, get the label text and position
/**
* @param {any[]} labels
* @param {Array<Record<string, any>>} timescaleLabels
* @param {number} x
* @param {number} i
* @param {any[]} drawnLabels
*/
getLabel(labels, timescaleLabels, x, i, drawnLabels = [], fontSize = "12px", isLeafGroup = true) {
const w = this.w;
const rawLabel = typeof labels[i] === "undefined" ? "" : labels[i];
let label = rawLabel;
const xlbFormatter = w.formatters.xLabelFormatter;
const customFormatter = w.config.xaxis.labels.formatter;
let isBold = false;
const xFormat = new Formatters(this.w);
const timestamp = rawLabel;
if (isLeafGroup) {
label = /** @type {any} */
xFormat.xLabelFormat(
xlbFormatter,
rawLabel,
timestamp,
{
i,
dateFormatter: new DateTime(this.w).formatDate,
w
}
);
if (customFormatter !== void 0) {
label = customFormatter(rawLabel, labels[i], {
i,
dateFormatter: new DateTime(this.w).formatDate,
w
});
}
}
const determineHighestUnit = (unit) => {
let highestUnit = null;
timescaleLabels.forEach((t) => {
if (t.unit === "month") {
highestUnit = "year";
} else if (t.unit === "day") {
highestUnit = "month";
} else if (t.unit === "hour") {
highestUnit = "day";
} else if (t.unit === "minute") {
highestUnit = "hour";
}
});
return highestUnit === unit;
};
if (timescaleLabels.length > 0) {
isBold = determineHighestUnit(timescaleLabels[i].unit);
x = timescaleLabels[i].position;
label = timescaleLabels[i].value;
} else {
if (w.config.xaxis.type === "datetime" && customFormatter === void 0) {
label = "";
}
}
if (typeof label === "undefined") label = "";
label = Array.isArray(label) ? label : label.toString();
const graphics = new Graphics(this.w);
let textRect = {};
if (w.layout.rotateXLabels && isLeafGroup) {
textRect = graphics.getTextRects(
label,
parseInt(fontSize, 10).toString(),
null,
`rotate(${w.config.xaxis.labels.rotate} 0 0)`,
false
);
} else {
textRect = graphics.getTextRects(label, parseInt(fontSize, 10).toString());
}
const allowDuplicatesInTimeScale = !w.config.xaxis.labels.showDuplicates && this.timeScale;
if (!Array.isArray(label) && (String(label) === "NaN" || drawnLabels.indexOf(label) >= 0 && allowDuplicatesInTimeScale)) {
label = "";
}
return {
x,
text: label,
textRect,
isBold
};
}
/**
* @param {number} i
* @param {any} label
* @param {number} labelsLen
*/
checkLabelBasedOnTickamount(i, label, labelsLen) {
const w = this.w;
let ticks = w.config.xaxis.tickAmount;
if (ticks === "dataPoints") ticks = Math.round(w.layout.gridWidth / 120);
if (ticks > labelsLen) return label;
const tickMultiple = Math.round(labelsLen / (ticks + 1));
if (i % tickMultiple === 0) {
return label;
} else {
label.text = "";
}
return label;
}
/**
* @param {number} i
* @param {any} label
* @param {number} labelsLen
* @param {any[]} drawnLabels
* @param {Array<Record<string, any>>} drawnLabelsRects
*/
checkForOverflowingLabels(i, label, labelsLen, drawnLabels, drawnLabelsRects) {
const w = this.w;
if (i === 0) {
if (w.globals.skipFirstTimelinelabel) {
label.text = "";
}
}
if (i === labelsLen - 1) {
if (w.globals.skipLastTimelinelabel) {
label.text = "";
}
}
if (w.config.xaxis.labels.hideOverlappingLabels && drawnLabels.length > 0) {
const prev = drawnLabelsRects[drawnLabelsRects.length - 1];
if (w.config.xaxis.labels.trim && w.config.xaxis.type !== "datetime") {
return label;
}
if (
/** @type {any} */
label.x < prev.textRect.width / (w.layout.rotateXLabels ? Math.abs(w.config.xaxis.labels.rotate) / 12 : 1.01) + prev.x
) {
label.text = "";
}
}
return label;
}
/**
* @param {number} i
* @param {any[]} labels
*/
checkForReversedLabels(i, labels) {
const w = this.w;
if (w.config.yaxis[i] && w.config.yaxis[i].reversed) {
labels.reverse();
}
return labels;
}
/**
* @param {number} index
*/
yAxisAllSeriesCollapsed(index) {
const gl = this.w.globals;
return !gl.seriesYAxisMap[index].some((si) => {
return gl.collapsedSeriesIndices.indexOf(si) === -1;
});
}
// Method to translate annotation.yAxisIndex values from
// seriesName-as-a-string values to seriesName-as-an-array values (old style
// series mapping to new style).
/**
* @param {number} index
*/
translateYAxisIndex(index) {
const w = this.w;
const gl = w.globals;
const yaxis = w.config.yaxis;
const newStyle = w.seriesData.series.length > yaxis.length || /**
* @param {Record<string, any>} a
*/
yaxis.some((a) => Array.isArray(a.seriesName));
if (newStyle) {
return index;
} else {
return gl.seriesYAxisReverseMap[index];
}
}
/**
* @param {number} index
*/
isYAxisHidden(index) {
const w = this.w;
const yaxis = w.config.yaxis[index];
if (!yaxis.show || this.yAxisAllSeriesCollapsed(index)) {
return true;
}
if (!yaxis.showForNullSeries) {
const seriesIndices = w.globals.seriesYAxisMap[index];
const coreUtils = new CoreUtils(this.w);
return seriesIndices.every((si) => coreUtils.isSeriesNull(si));
}
return false;
}
// get the label color for y-axis
// realIndex is the actual series index, while i is the tick Index
/**
* @param {string[]} yColors
* @param {number} realIndex
*/
getYAxisForeColor(yColors, realIndex) {
var _a;
const w = this.w;
if (Array.isArray(yColors) && w.globals.yAxisScale[realIndex]) {
(_a = this.theme) == null ? void 0 : _a.pushExtraColors(
yColors,
w.globals.yAxisScale[realIndex].result.length,
false
);
}
return yColors;
}
/**
* @param {number} x
* @param {number} tickAmount
* @param {Record<string, any>} axisBorder
* @param {Record<string, any>} axisTicks
* @param {number} realIndex
* @param {any} labelsDivider
* @param {any} elYaxis
*/
drawYAxisTicks(x, tickAmount, axisBorder, axisTicks, realIndex, labelsDivider, elYaxis) {
const w = this.w;
const graphics = new Graphics(this.w);
let tY = w.layout.translateY + w.config.yaxis[realIndex].labels.offsetY;
if (w.globals.isBarHorizontal) {
tY = 0;
} else if (w.config.chart.type === "heatmap") {
tY += labelsDivider / 2;
}
if (axisTicks.show && tickAmount > 0) {
if (w.config.yaxis[realIndex].opposite === true) x = x + axisTicks.width;
for (let i = tickAmount; i >= 0; i--) {
const elTick = graphics.drawLine(
x + axisBorder.offsetX - axisTicks.width + axisTicks.offsetX,
tY + axisTicks.offsetY,
x + axisBorder.offsetX + axisTicks.offsetX,
tY + axisTicks.offsetY,
axisTicks.color
);
elYaxis.add(elTick);
tY += labelsDivider;
}
}
}
}
class XAxis {
/**
* @param {import('../../types/internal').ChartStateW} w
* @param {import('../../types/internal').ChartContext} ctx
* @param {any} [elgrid]
*/
constructor(w, ctx, elgrid) {
this.w = w;
this.ctx = ctx;
this.elgrid = elgrid;
this.axesUtils = new AxesUtils(w, {
theme: ctx.theme,
timeScale: ctx.timeScale
});
this.xaxisLabels = w.labelData.labels.slice();
if (w.labelData.timescaleLabels.length > 0 && !w.globals.isBarHorizontal) {
this.xaxisLabels = w.labelData.timescaleLabels.slice();
}
if (w.config.xaxis.overwriteCategories) {
this.xaxisLabels = w.config.xaxis.overwriteCategories;
}
this.drawnLabels = [];
this.drawnLabelsRects = [];
if (w.config.xaxis.position === "top") {
this.offY = 0;
} else {
this.offY = w.layout.gridHeight;
}
this.offY = this.offY + w.config.xaxis.axisBorder.offsetY;
this.isCategoryBarHorizontal = w.config.chart.type === "bar" && w.config.plotOptions.bar.horizontal;
this.xaxisFontSize = w.config.xaxis.labels.style.fontSize;
this.xaxisFontFamily = w.config.xaxis.labels.style.fontFamily;
this.xaxisForeColors = w.config.xaxis.labels.style.colors;
this.xaxisBorderWidth = w.config.xaxis.axisBorder.width;
if (this.isCategoryBarHorizontal) {
this.xaxisBorderWidth = w.config.yaxis[0].axisBorder.width.toString();
}
if (String(this.xaxisBorderWidth).indexOf("%") > -1) {
this.xaxisBorderWidth = w.layout.gridWidth * parseInt(this.xaxisBorderWidth, 10) / 100;
} else {
this.xaxisBorderWidth = parseInt(this.xaxisBorderWidth, 10);
}
this.xaxisBorderHeight = w.config.xaxis.axisBorder.height;
this.yaxis = w.config.yaxis[0];
}
drawXaxis() {
const w = this.w;
const graphics = new Graphics(this.w);
const elXaxis = graphics.group({
class: "apexcharts-xaxis",
transform: `translate(${w.config.xaxis.offsetX}, ${w.config.xaxis.offsetY})`
});
const elXaxisTexts = graphics.group({
class: "apexcharts-xaxis-texts-g",
transform: `translate(${w.layout.translateXAxisX}, ${w.layout.translateXAxisY})`
});
elXaxis.add(elXaxisTexts);
let labels = [];
for (let i = 0; i < this.xaxisLabels.length; i++) {
labels.push(this.xaxisLabels[i]);
}
this.drawXAxisLabelAndGroup(
true,
graphics,
elXaxisTexts,
labels,
w.axisFlags.isXNumeric,
(i, colWidth) => colWidth
);
if (w.labelData.hasXaxisGroups) {
const labelsGroup = w.labelData.groups;
labels = [];
for (let i = 0; i < labelsGroup.length; i++) {
labels.push(labelsGroup[i].title);
}
const overwriteStyles = (
/** @type {any} */
{}
);
if (w.config.xaxis.group.style) {
overwriteStyles.xaxisFontSize = w.config.xaxis.group.style.fontSize;
overwriteStyles.xaxisFontFamily = w.config.xaxis.group.style.fontFamily;
overwriteStyles.xaxisForeColors = w.config.xaxis.group.style.colors;
overwriteStyles.fontWeight = w.config.xaxis.group.style.fontWeight;
overwriteStyles.cssClass = w.config.xaxis.group.style.cssClass;
}
this.drawXAxisLabelAndGroup(
false,
graphics,
elXaxisTexts,
labels,
false,
(i, colWidth) => labelsGroup[i].cols * colWidth,
overwriteStyles
);
}
if (w.config.xaxis.title.text !== void 0) {
const elXaxisTitle = graphics.group({
class: "apexcharts-xaxis-title"
});
const elXAxisTitleText = graphics.drawText({
x: w.layout.gridWidth / 2 + w.config.xaxis.title.offsetX,
y: this.offY + parseFloat(this.xaxisFontSize) + (w.config.xaxis.position === "bottom" ? w.layout.xAxisLabelsHeight : -w.layout.xAxisLabelsHeight - 10) + w.config.xaxis.title.offsetY,
text: w.config.xaxis.title.text,
textAnchor: "middle",
fontSize: w.config.xaxis.title.style.fontSize,
fontFamily: w.config.xaxis.title.style.fontFamily,
fontWeight: w.config.xaxis.title.style.fontWeight,
foreColor: w.config.xaxis.title.style.color,
cssClass: "apexcharts-xaxis-title-text " + w.config.xaxis.title.style.cssClass
});
elXaxisTitle.add(elXAxisTitleText);
elXaxis.add(elXaxisTitle);
}
if (w.config.xaxis.axisBorder.show) {
const offX = w.globals.barPadForNumericAxis;
const elHorzLine = graphics.drawLine(
w.globals.padHorizontal + w.config.xaxis.axisBorder.offsetX - offX,
this.offY,
this.xaxisBorderWidth + offX,
this.offY,
w.config.xaxis.axisBorder.color,
0,
this.xaxisBorderHeight
);
if (this.elgrid && this.elgrid.elGridBorders && w.config.grid.show) {
this.elgrid.elGridBorders.add(elHorzLine);
} else {
elXaxis.add(elHorzLine);
}
}
return elXaxis;
}
/**
* @param {Record<string, any>} [overwriteStyles]
* @param {boolean} isLeafGroup
* @param {import('../Graphics').default} graphics
* @param {any} elXaxisTexts
* @param {any[]} labels
* @param {boolean} isXNumeric
* @param {any} colWidthCb
*/
drawXAxisLabelAndGroup(isLeafGroup, graphics, elXaxisTexts, labels, isXNumeric, colWidthCb, overwriteStyles = {}) {
const drawnLabels = [];
const drawnLabelsRects = [];
const w = this.w;
const xaxisFontSize = overwriteStyles.xaxisFontSize || this.xaxisFontSize;
const xaxisFontFamily = overwriteStyles.xaxisFontFamily || this.xaxisFontFamily;
const xaxisForeColors = overwriteStyles.xaxisForeColors || this.xaxisForeColors;
const fontWeight = overwriteStyles.fontWeight || w.config.xaxis.labels.style.fontWeight;
const cssClass = overwriteStyles.cssClass || w.config.xaxis.labels.style.cssClass;
let colWidth;
let xPos = w.globals.padHorizontal;
const labelsLen = labels.length;
let dataPoints = w.config.xaxis.type === "category" ? w.globals.dataPoints : labelsLen;
if (dataPoints === 0 && labelsLen > dataPoints) dataPoints = labelsLen;
if (isXNumeric) {
const len = Math.max(
Number(w.config.xaxis.tickAmount) || 1,
dataPoints > 1 ? dataPoints - 1 : dataPoints
);
colWidth = w.layout.gridWidth / Math.min(len, labelsLen - 1);
xPos = xPos + colWidthCb(0, colWidth) / 2 + w.config.xaxis.labels.offsetX;
} else {
colWidth = w.layout.gridWidth / dataPoints;
xPos = xPos + colWidthCb(0, colWidth) + w.config.xaxis.labels.offsetX;
}
for (let i = 0; i <= labelsLen - 1; i++) {
let x = xPos - colWidthCb(i, colWidth) / 2 + w.config.xaxis.labels.offsetX;
if (i === 0 && labelsLen === 1 && colWidth / 2 === xPos && dataPoints === 1) {
x = w.layout.gridWidth / 2;
}
let label = this.axesUtils.getLabel(
labels,
w.labelData.timescaleLabels,
x,
i,
drawnLabels,
xaxisFontSize,
isLeafGroup
);
let offsetYCorrection = 28;
if (w.layout.rotateXLabels && isLeafGroup) {
offsetYCorrection = 22;
}
if (w.config.xaxis.title.text && w.config.xaxis.position === "top") {
offsetYCorrection += parseFloat(w.config.xaxis.title.style.fontSize) + 2;
}
if (!isLeafGroup) {
offsetYCorrection = offsetYCorrection + parseFloat(xaxisFontSize) + (w.layout.xAxisLabelsHeight - w.layout.xAxisGroupLabelsHeight) + (w.layout.rotateXLabels ? 10 : 0);
}
const isCategoryTickAmounts = typeof w.config.xaxis.tickAmount !== "undefined" && w.config.xaxis.tickAmount !== "dataPoints" && w.config.xaxis.type !== "datetime";
if (isCategoryTickAmounts) {
label = this.axesUtils.checkLabelBasedOnTickamount(i, label, labelsLen);
} else {
label = this.axesUtils.checkForOverflowingLabels(
i,
label,
labelsLen,
drawnLabels,
drawnLabelsRects
);
}
const getCatForeColor = () => {
return isLeafGroup && w.config.xaxis.convertedCatToNumeric ? xaxisForeColors[w.globals.minX + i - 1] : xaxisForeColors[i];
};
if (w.config.xaxis.labels.show) {
const elText = graphics.drawText({
x: label.x,
y: this.offY + w.config.xaxis.labels.offsetY + offsetYCorrection - (w.config.xaxis.position === "top" ? w.layout.xAxisHeight + w.config.xaxis.axisTicks.height - 2 : 0),
text: label.text,
textAnchor: "middle",
fontWeight: label.isBold ? 600 : fontWeight,
fontSize: xaxisFontSize,
fontFamily: xaxisFontFamily,
foreColor: Array.isArray(xaxisForeColors) ? getCatForeColor() : xaxisForeColors,
isPlainText: false,
cssClass: (isLeafGroup ? "apexcharts-xaxis-label " : "apexcharts-xaxis-group-label ") + cssClass
});
elXaxisTexts.add(elText);
elText.on("click", (e) => {
if (typeof w.config.chart.events.xAxisLabelClick === "function") {
const opts = Object.assign({}, w, {
labelIndex: i
});
w.config.chart.events.xAxisLabelClick(e, this.ctx, opts);
}
});
if (isLeafGroup) {
const elTooltipTitle = BrowserAPIs.createElementNS(SVGNS, "title");
elTooltipTitle.textContent = Array.isArray(label.text) ? label.text.join(" ") : label.text;
elText.node.appendChild(elTooltipTitle);
if (label.text !== "") {
drawnLabels.push(label.text);
drawnLabelsRects.push(label);
}
}
}
if (i < labelsLen - 1) {
xPos = xPos + colWidthCb(i + 1, colWidth);
}
}
}
// this actually becomes the vertical axis (for bar charts)
/**
* @param {number} realIndex
*/
drawXaxisInversed(realIndex) {
const w = this.w;
const graphics = new Graphics(this.w);
const translateYAxisX = w.config.yaxis[0].opposite ? w.globals.translateYAxisX[realIndex] : 0;
const elYaxis = graphics.group({
class: "apexcharts-yaxis apexcharts-xaxis-inversed",
rel: realIndex
});
const elYaxisTexts = graphics.group({
class: "apexcharts-yaxis-texts-g apexcharts-xaxis-inversed-texts-g",
transform: "translate(" + translateYAxisX + ", 0)"
});
elYaxis.add(elYaxisTexts);
const labels = [];
if (w.config.yaxis[realIndex].show) {
for (let i = 0; i < this.xaxisLabels.length; i++) {
labels.push(this.xaxisLabels[i]);
}
}
const colHeight = w.layout.gridHeight / labels.length;
let yPos = -(colHeight / 2.2);
const lbFormatter = w.formatters.yLabelFormatters[0];
const ylabels = w.config.yaxis[0].labels;
if (ylabels.show) {
for (let i = 0; i <= labels.length - 1; i++) {
let label = typeof labels[i] === "undefined" ? "" : labels[i];
label = lbFormatter(label, {
seriesIndex: realIndex,
dataPointIndex: i,
w
});
const yColors = this.axesUtils.getYAxisForeColor(
ylabels.style.colors,
realIndex
);
const getForeColor = () => {
return Array.isArray(yColors) ? yColors[i] : yColors;
};
let multiY = 0;
if (Array.isArray(label)) {
multiY = label.length / 2 * parseInt(ylabels.style.fontSize, 10);
}
let offsetX = ylabels.offsetX - 15;
let textAnchor = "end";
if (this.yaxis.opposite) {
textAnchor = "start";
}
if (w.config.yaxis[0].labels.align === "left") {
offsetX = ylabels.offsetX;
textAnchor = "start";
} else if (w.config.yaxis[0].labels.align === "center") {
offsetX = ylabels.offsetX;
textAnchor = "middle";
} else if (w.config.yaxis[0].labels.align === "right") {
textAnchor = "end";
}
const elLabel = graphics.drawText({
x: offsetX,
y: yPos + colHeight + ylabels.offsetY - multiY,
text: label,
textAnchor,
foreColor: getForeColor(),
fontSize: ylabels.style.fontSize,
fontFamily: ylabels.style.fontFamily,
fontWeight: ylabels.style.fontWeight,
isPlainText: false,
cssClass: "apexcharts-yaxis-label " + ylabels.style.cssClass,
maxWidth: ylabels.maxWidth
});
elYaxisTexts.add(elLabel);
elLabel.on("click", (e) => {
if (typeof w.config.chart.events.xAxisLabelClick === "function") {
const opts = Object.assign({}, w, {
labelIndex: i
});
w.config.chart.events.xAxisLabelClick(e, this.ctx, opts);
}
});
const elTooltipTitle = BrowserAPIs.createElementNS(SVGNS, "title");
elTooltipTitle.textContent = Array.isArray(label) ? label.join(" ") : label;
elLabel.node.appendChild(elTooltipTitle);
if (w.config.yaxis[realIndex].labels.rotate !== 0) {
const labelRotatingCenter = graphics.rotateAroundCenter(elLabel.node);
elLabel.node.setAttribute(
"transform",
`rotate(${w.config.yaxis[realIndex].labels.rotate} 0 ${labelRotatingCenter.y})`
);
}
yPos = yPos + colHeight;
}
}
if (w.config.yaxis[0].title.text !== void 0) {
const elXaxisTitle = graphics.group({
class: "apexcharts-yaxis-title apexcharts-xaxis-title-inversed",
transform: "translate(" + translateYAxisX + ", 0)"
});
const elXAxisTitleText = graphics.drawText({
x: w.config.yaxis[0].title.offsetX,
y: w.layout.gridHeight / 2 + w.config.yaxis[0].title.offsetY,
text: w.config.yaxis[0].title.text,
textAnchor: "middle",
foreColor: w.config.yaxis[0].title.style.color,
fontSize: w.config.yaxis[0].title.style.fontSize,
fontWeight: w.config.yaxis[0].title.style.fontWeight,
fontFamily: w.config.yaxis[0].title.style.fontFamily,
cssClass: "apexcharts-yaxis-title-text " + w.config.yaxis[0].title.style.cssClass
});
elXaxisTitle.add(elXAxisTitleText);
elYaxis.add(elXaxisTitle);
}
let offX = 0;
if (this.isCategoryBarHorizontal && w.config.yaxis[0].opposite) {
offX = w.layout.gridWidth;
}
const axisBorder = w.config.xaxis.axisBorder;
if (axisBorder.show) {
const elVerticalLine = graphics.drawLine(
w.globals.padHorizontal + axisBorder.offsetX + offX,
1 + axisBorder.offsetY,
w.globals.padHorizontal + axisBorder.offsetX + offX,
w.layout.gridHeight + axisBorder.offsetY,
axisBorder.color,
0
);
if (this.elgrid && this.elgrid.elGridBorders && w.config.grid.show) {
this.elgrid.elGridBorders.add(elVerticalLine);
} else {
elYaxis.add(elVerticalLine);
}
}
if (w.config.yaxis[0].axisTicks.show) {
this.axesUtils.drawYAxisTicks(
offX,
labels.length,
w.config.yaxis[0].axisBorder,
w.config.yaxis[0].axisTicks,
0,
colHeight,
elYaxis
);
}
return elYaxis;
}
/**
* @param {number} x1
* @param {number} y2
* @param {any} appendToElement
*/
drawXaxisTicks(x1, y2, appendToElement) {
const w = this.w;
const x2 = x1;
if (x1 < 0 || x1 - 2 > w.layout.gridWidth) return;
const y1 = this.offY + w.config.xaxis.axisTicks.offsetY;
y2 = y2 + y1 + w.config.xaxis.axisTicks.height;
if (w.config.xaxis.position === "top") {
y2 = y1 - w.config.xaxis.axisTicks.height;
}
if (w.config.xaxis.axisTicks.show) {
const graphics = new Graphics(this.w);
const line = graphics.drawLine(
x1 + w.config.xaxis.axisTicks.offsetX,
y1 + w.config.xaxis.offsetY,
x2 + w.config.xaxis.axisTicks.offsetX,
y2 + w.config.xaxis.offsetY,
w.config.xaxis.axisTicks.color
);
appendToElement.add(line);
line.node.classList.add("apexcharts-xaxis-tick");
}
}
getXAxisTicksPositions() {
const w = this.w;
const xAxisTicksPositions = [];
const xCount = this.xaxisLabels.length;
let x1 = w.globals.padHorizontal;
if (w.labelData.timescaleLabels.length > 0) {
for (let i = 0; i < xCount; i++) {
x1 = this.xaxisLabels[i].position;
xAxisTicksPositions.push(x1);
}
} else {
const xCountForCategoryCharts = xCount;
for (let i = 0; i < xCountForCategoryCharts; i++) {
let x1Count = xCountForCategoryCharts;
if (w.axisFlags.isXNumeric && w.config.chart.type !== "bar") {
x1Count -= 1;
}
x1 = x1 + w.layout.gridWidth / x1Count;
xAxisTicksPositions.push(x1);
}
}
return xAxisTicksPositions;
}
// to rotate x-axis labels or to put ... for longer text in xaxis
xAxisLabelCorrections() {
var _a, _b, _c;
const w = this.w;
const graphics = new Graphics(this.w);
const xAxis = w.dom.baseEl.querySelector(".apexcharts-xaxis-texts-g");
const xAxisTexts = w.dom.baseEl.querySelectorAll(
".apexcharts-xaxis-texts-g text:not(.apexcharts-xaxis-group-label)"
);
const yAxisTextsInversed = w.dom.baseEl.querySelectorAll(
".apexcharts-yaxis-inversed text"
);
const xAxisTextsInversed = w.dom.baseEl.querySelectorAll(
".apexcharts-xaxis-inversed-texts-g text tspan"
);
if (w.layout.rotateXLabels || w.config.xaxis.labels.rotateAlways) {
for (let xat = 0; xat < xAxisTexts.length; xat++) {
const textRotatingCenter = graphics.rotateAroundCenter(xAxisTexts[xat]);
textRotatingCenter.y = textRotatingCenter.y - 1;
textRotatingCenter.x = textRotatingCenter.x + 1;
xAxisTexts[xat].setAttribute(
"transform",
`rotate(${w.config.xaxis.labels.rotate} ${textRotatingCenter.x} ${textRotatingCenter.y})`
);
xAxisTexts[xat].setAttribute("text-anchor", `end`);
xAxis == null ? void 0 : xAxis.setAttribute("transform", `translate(0, ${-10})`);
const tSpan = xAxisTexts[xat].childNodes;
if (w.config.xaxis.labels.trim) {
Array.prototype.forEach.call(tSpan, (ts) => {
graphics.placeTextWithEllipsis(
ts,
ts.textContent,
w.layout.xAxisLabelsHeight - (w.config.legend.position === "bottom" ? 20 : 10)
);
});
}
}
} else {
const width = w.layout.gridWidth / (w.labelData.labels.length + 1);
for (let xat = 0; xat < xAxisTexts.length; xat++) {
const tSpan = xAxisTexts[xat].childNodes;
if (w.config.xaxis.labels.trim && w.config.xaxis.type !== "datetime") {
Array.prototype.forEach.call(tSpan, (ts) => {
graphics.placeTextWithEllipsis(ts, ts.textContent, width);
});
}
}
}
if (yAxisTextsInversed.length > 0) {
const firstLabelPosX = (
/** @type {SVGGraphicsElement} */
yAxisTextsInversed[yAxisTextsInversed.length - 1].getBBox()
);
const lastLabelPosX = (
/** @type {SVGGraphicsElement} */
yAxisTextsInversed[0].getBBox()
);
if (firstLabelPosX.x < -20) {
(_a = yAxisTextsInversed[yAxisTextsInversed.length - 1].parentNode) == null ? void 0 : _a.removeChild(
yAxisTextsInversed[yAxisTextsInversed.length - 1]
);
}
if (lastLabelPosX.x + lastLabelPosX.width > w.layout.gridWidth && !w.globals.isBarHorizontal) {
(_b = yAxisTextsInversed[0].parentNode) == null ? void 0 : _b.removeChild(yAxisTextsInversed[0]);
}
for (let xat = 0; xat < xAxisTextsInversed.length; xat++) {
graphics.placeTextWithEllipsis(
xAxisTextsInversed[xat],
(_c = xAxisTextsInversed[xat].textContent) != null ? _c : "",
w.config.yaxis[0].labels.maxWidth - (w.config.yaxis[0].title.text ? parseFloat(w.config.yaxis[0].title.style.fontSize) * 2 : 0) - 15
);
}
}
}
// renderXAxisBands() {
// let w = this.w;
// let plotBand = document.createElementNS(SVGNS, 'rect')
// w.dom.elGraphical.add(plotBand)
// }
}
class Grid {
/**
* @param {import('../../types/internal').ChartStateW} w
* @param {import('../../types/internal').ChartContext} ctx
*/
constructor(w, ctx) {
this.w = w;
this.ctx = ctx;
this.xaxisLabels = w.labelData.labels.slice();
this.axesUtils = new AxesUtils(ctx.w, {
theme: ctx.theme,
timeScale: ctx.timeScale
});
this.isRangeBar = w.rangeData.seriesRange.length && w.globals.isBarHorizontal;
if (w.labelData.timescaleLabels.length > 0) {
this.xaxisLabels = w.labelData.timescaleLabels.slice();
}
}
/**
* @param {any} elGrid
*/
drawGridArea(elGrid = null) {
const w = this.w;
const graphics = new Graphics(this.w);
if (!elGrid) {
elGrid = graphics.group({ class: "apexcharts-grid" });
}
const elVerticalLine = graphics.drawLine(
w.globals.padHorizontal,
1,
w.globals.padHorizontal,
w.layout.gridHeight,
"transparent"
);
const elHorzLine = graphics.drawLine(
w.globals.padHorizontal,
w.layout.gridHeight,
w.layout.gridWidth,
w.layout.gridHeight,
"transparent"
);
elGrid.add(elHorzLine);
elGrid.add(elVerticalLine);
return elGrid;
}
drawGrid() {
const gl = this.w.globals;
if (gl.axisCharts) {
const elgrid = this.renderGrid();
this.drawGridArea(elgrid.el);
return elgrid;
}
return null;
}
createGridMask() {
const w = this.w;
const gl = w.globals;
const graphics = new Graphics(this.w);
const strokeSize = Array.isArray(w.config.stroke.width) ? Math.max(...w.config.stroke.width) : w.config.stroke.width;
const createClipPath = (id) => {
const clipPath = BrowserAPIs.createElementNS(SVGNS, "clipPath");
clipPath.setAttribute("id", id);
return clipPath;
};
w.dom.elGridRectMask = createClipPath(`gridRectMask${gl.cuid}`);
w.dom.elGridRectBarMask = createClipPath(`gridRectBarMask${gl.cuid}`);
w.dom.elGridRectMarkerMask = createClipPath(`gridRectMarkerMask${gl.cuid}`);
w.dom.elForecastMask = createClipPath(`forecastMask${gl.cuid}`);
w.dom.elNonForecastMask = createClipPath(`nonForecastMask${gl.cuid}`);
const hasBar = ["bar", "rangeBar", "candlestick", "boxPlot"].includes(
w.config.chart.type
) || w.globals.comboBarCount > 0;
let barWidthLeft = 0;
let barWidthRight = 0;
if (hasBar && w.axisFlags.isXNumeric && !w.globals.isBarHorizontal) {
barWidthLeft = Math.max(
w.config.grid.padding.left,
gl.barPadForNumericAxis
);
barWidthRight = Math.max(
w.config.grid.padding.right,
gl.barPadForNumericAxis
);
}
w.dom.elGridRect = graphics.drawRect(
-strokeSize / 2 - 2,
-strokeSize / 2 - 2,
w.layout.gridWidth + strokeSize + 4,
w.layout.gridHeight + strokeSize + 4,
0,
"#fff"
);
w.dom.elGridRectBar = graphics.drawRect(
-strokeSize / 2 - barWidthLeft - 2,
-strokeSize / 2 - 2,
w.layout.gridWidth + strokeSize + barWidthRight + barWidthLeft + 4,
w.layout.gridHeight + strokeSize + 4,
0,
"#fff"
);
const markerSize = w.globals.markers.largestSize;
w.dom.elGridRectMarker = graphics.drawRect(
Math.min(-strokeSize / 2 - barWidthLeft - 2, -markerSize),
-markerSize,
w.layout.gridWidth + Math.max(strokeSize + barWidthRight + barWidthLeft + 4, markerSize * 2),
w.layout.gridHeight + markerSize * 2,
0,
"#fff"
);
w.dom.elGridRectMask.appendChild(w.dom.elGridRect.node);
w.dom.elGridRectBarMask.appendChild(w.dom.elGridRectBar.node);
w.dom.elGridRectMarkerMask.appendChild(w.dom.elGridRectMarker.node);
const defs = w.dom.elDefs.node;
defs.appendChild(w.dom.elGridRectMask);
defs.appendChild(w.dom.elGridRectBarMask);
defs.appendChild(w.dom.elGridRectMarkerMask);
defs.appendChild(w.dom.elForecastMask);
defs.appendChild(w.dom.elNonForecastMask);
}
/** @param {{i: any, x1: any, y1: any, x2: any, y2: any, xCount: any, parent: any}} opts */
_drawGridLines({ i, x1, y1, x2, y2, xCount, parent }) {
const w = this.w;
const shouldDraw = () => {
if (i === 0 && w.globals.skipFirstTimelinelabel) return false;
if (i === xCount - 1 && w.globals.skipLastTimelinelabel && !w.config.xaxis.labels.formatter)
return false;
if (w.config.chart.type === "radar") return false;
return true;
};
if (shouldDraw()) {
if (w.config.grid.xaxis.lines.show) {
this._drawGridLine({ i, x1, y1, x2, y2, xCount, parent });
}
let y_2 = 0;
if (w.labelData.hasXaxisGroups && w.config.xaxis.tickPlacement === "between") {
const groups = w.labelData.groups;
if (groups) {
let gacc = 0;
for (let gi = 0; gacc < i && gi < groups.length; gi++) {
gacc += groups[gi].cols;
}
if (gacc === i) {
y_2 = w.layout.xAxisLabelsHeight * 0.6;
}
}
}
const xAxis = new XAxis(this.w, this.ctx);
xAxis.drawXaxisTicks(x1, y_2, w.dom.elGraphical);
}
}
/** @param {{i: any, x1: any, y1: any, x2: any, y2: any, xCount: any, parent: any}} opts */
_drawGridLine({ i, x1, y1, x2, y2, xCount, parent }) {
const w = this.w;
const isHorzLine = parent.node.classList.contains(
"apexcharts-gridlines-horizontal"
);
const offX = w.globals.barPadForNumericAxis;
const excludeBorders = y1 === 0 && y2 === 0 || x1 === 0 && x2 === 0 || y1 === w.layout.gridHeight && y2 === w.layout.gridHeight || w.globals.isBarHorizontal && (i === 0 || i === xCount - 1);
const graphics = new Graphics(this.w);
const line = graphics.drawLine(
x1 - (isHorzLine ? offX : 0),
y1,
x2 + (isHorzLine ? offX : 0),
y2,
w.config.grid.borderColor,
w.config.grid.strokeDashArray
);
line.node.classList.add("apexcharts-gridline");
if (excludeBorders && w.config.grid.show) {
this.elGridBorders.add(line);
} else {
parent.add(line);
}
}
/** @param {{c: any, x1: any, y1: any, x2: any, y2: any, type: any}} opts */
_drawGridBandRect({ c, x1, y1, x2, y2, type }) {
const w = this.w;
const graphics = new Graphics(this.w);
const offX = w.globals.barPadForNumericAxis;
const color = w.config.grid[type].colors[c];
const rect = graphics.drawRect(
x1 - (type === "row" ? offX : 0),
y1,
x2 + (type === "row" ? offX * 2 : 0),
y2,
0,
color,
w.config.grid[type].opacity
);
this.elg.add(rect);
rect.attr("clip-path", `url(#gridRectMask${w.globals.cuid})`);
rect.node.classList.add(`apexcharts-grid-${type}`);
}
/** @param {{xCount: any, tickAmount: any}} opts */
_drawXYLines({ xCount, tickAmount }) {
var _a;
const w = this.w;
const datetimeLines = ({ xC, x1, y1, x2, y2 }) => {
for (let i = 0; i < xC; i++) {
x1 = /** @type {any} */
this.xaxisLabels[i].position;
x2 = /** @type {any} */
this.xaxisLabels[i].position;
this._drawGridLines({
i,
x1,
y1,
x2,
y2,
xCount,
parent: this.elgridLinesV
});
}
};
const categoryLines = ({ xC, x1, y1, x2, y2 }) => {
for (let i = 0; i < xC + (w.axisFlags.isXNumeric ? 0 : 1); i++) {
if (i === 0 && xC === 1 && w.globals.dataPoints === 1) {
x1 = w.layout.gridWidth / 2;
x2 = x1;
}
this._drawGridLines({
i,
x1,
y1,
x2,
y2,
xCount,
parent: this.elgridLinesV
});
x1 += w.layout.gridWidth / (w.axisFlags.isXNumeric ? xC - 1 : xC);
x2 = x1;
}
};
if (w.config.grid.xaxis.lines.show || w.config.xaxis.axisTicks.show) {
const x1 = w.globals.padHorizontal;
const y1 = 0;
let x2;
const y2 = w.layout.gridHeight;
if (w.labelData.timescaleLabels.length) {
datetimeLines({ xC: xCount, x1, y1, x2, y2 });
} else {
if (w.axisFlags.isXNumeric) {
xCount = (_a = w.globals.xAxisScale) == null ? void 0 : _a.result.length;
}
categoryLines({ xC: xCount, x1, y1, x2, y2 });
}
}
if (w.config.grid.yaxis.lines.show) {
const x1 = 0;
let y1 = 0;
let y2 = 0;
const x2 = w.layout.gridWidth;
let tA = tickAmount + 1;
if (this.isRangeBar) {
tA = w.labelData.labels.length;
}
for (let i = 0; i < tA + (this.isRangeBar ? 1 : 0); i++) {
this._drawGridLine({
i,
xCount: tA + (this.isRangeBar ? 1 : 0),
x1,
y1,
x2,
y2,
parent: this.elgridLinesH
});
y1 += w.layout.gridHeight / (this.isRangeBar ? tA : tickAmount);
y2 = y1;
}
}
}
/** @param {{ xCount?: any, tickAmount?: any }} opts */
_drawInvertedXYLines({ xCount }) {
const w = this.w;
if (w.config.grid.xaxis.lines.show || w.config.xaxis.axisTicks.show) {
let x1 = w.globals.padHorizontal;
const y1 = 0;
let x2;
const y2 = w.layout.gridHeight;
for (let i = 0; i < xCount + 1; i++) {
if (w.config.grid.xaxis.lines.show) {
this._drawGridLine({
i,
xCount: xCount + 1,
x1,
y1,
x2,
y2,
parent: this.elgridLinesV
});
}
const xAxis = new XAxis(this.w, this.ctx);
xAxis.drawXaxisTicks(x1, 0, w.dom.elGraphical);
x1 += w.layout.gridWidth / xCount;
x2 = x1;
}
}
if (w.config.grid.yaxis.lines.show) {
const x1 = 0;
let y1 = 0;
let y2 = 0;
const x2 = w.layout.gridWidth;
for (let i = 0; i < w.globals.dataPoints + 1; i++) {
this._drawGridLine({
i,
xCount: w.globals.dataPoints + 1,
x1,
y1,
x2,
y2,
parent: this.elgridLinesH
});
y1 += w.layout.gridHeight / w.globals.dataPoints;
y2 = y1;
}
}
}
renderGrid() {
var _a, _b, _c;
const w = this.w;
const gl = w.globals;
const graphics = new Graphics(this.w);
this.elg = graphics.group({ class: "apexcharts-grid" });
this.elgridLinesH = graphics.group({
class: "apexcharts-gridlines-horizontal"
});
this.elgridLinesV = graphics.group({
class: "apexcharts-gridlines-vertical"
});
this.elGridBorders = graphics.group({ class: "apexcharts-grid-borders" });
this.elg.add(this.elgridLinesH);
this.elg.add(this.elgridLinesV);
if (!w.config.grid.show) {
this.elgridLinesV.hide();
this.elgridLinesH.hide();
this.elGridBorders.hide();
}
let gridAxisIndex = 0;
while (gridAxisIndex < gl.seriesYAxisMap.length && gl.ignoreYAxisIndexes.includes(gridAxisIndex)) {
gridAxisIndex++;
}
if (gridAxisIndex === gl.seriesYAxisMap.length) {
gridAxisIndex = 0;
}
let yTickAmount = gl.yAxisScale[gridAxisIndex].result.length - 1;
let xCount;
if (!gl.isBarHorizontal || this.isRangeBar) {
xCount = this.xaxisLabels.length;
if (this.isRangeBar) {
yTickAmount = w.labelData.labels.length;
if (w.config.xaxis.tickAmount && w.config.xaxis.labels.formatter) {
xCount = w.config.xaxis.tickAmount;
}
if (((_c = (_b = (_a = gl.yAxisScale) == null ? void 0 : _a[gridAxisIndex]) == null ? void 0 : _b.result) == null ? void 0 : _c.length) > 0 && w.config.xaxis.type !== "datetime") {
xCount = gl.yAxisScale[gridAxisIndex].result.length - 1;
}
}
this._drawXYLines({ xCount, tickAmount: yTickAmount });
} else {
xCount = yTickAmount;
yTickAmount = gl.xTickAmount;
this._drawInvertedXYLines({ xCount, tickAmount: yTickAmount });
}
this.drawGridBands(xCount, yTickAmount);
return {
el: this.elg,
elGridBorders: this.elGridBorders,
xAxisTickWidth: w.layout.gridWidth / xCount
};
}
/**
* @param {number} xCount
* @param {number} tickAmount
*/
drawGridBands(xCount, tickAmount) {
var _a, _b, _c, _d, _e;
const w = this.w;
const drawBands = (type, count, x1, y1, x2, y2) => {
for (let i = 0, c = 0; i < count; i++, c++) {
if (c >= w.config.grid[type].colors.length) {
c = 0;
}
this._drawGridBandRect({ c, x1, y1, x2, y2, type });
y1 += w.layout.gridHeight / tickAmount;
}
};
if (((_a = w.config.grid.row.colors) == null ? void 0 : _a.length) > 0) {
drawBands(
"row",
tickAmount,
0,
0,
w.layout.gridWidth,
w.layout.gridHeight / tickAmount
);
}
if (((_b = w.config.grid.column.colors) == null ? void 0 : _b.length) > 0) {
let xc = !w.globals.isBarHorizontal && w.config.xaxis.tickPlacement === "on" && (w.config.xaxis.type === "category" || w.config.xaxis.convertedCatToNumeric) ? xCount - 1 : xCount;
if (w.axisFlags.isXNumeric) {
xc = ((_d = (_c = w.globals.xAxisScale) == null ? void 0 : _c.result.length) != null ? _d : 1) - 1;
}
let x1 = w.globals.padHorizontal;
const y1 = 0;
let x2 = w.globals.padHorizontal + w.layout.gridWidth / xc;
const y2 = w.layout.gridHeight;
for (let i = 0, c = 0; i < xCount; i++, c++) {
if (c >= w.config.grid.column.colors.length) {
c = 0;
}
if (w.config.xaxis.type === "datetime") {
x1 = /** @type {any} */
this.xaxisLabels[i].position;
x2 = /** @type {any} */
(((_e = this.xaxisLabels[i + 1]) == null ? void 0 : _e.position) || w.layout.gridWidth) - /** @type {any} */
this.xaxisLabels[i].position;
}
this._drawGridBandRect({ c, x1, y1, x2, y2, type: "column" });
x1 += w.layout.gridWidth / xc;
}
}
}
}
class Scales {
/**
* @param {import('../types/internal').ChartStateW} w
*/
constructor(w) {
this.w = w;
this.coreUtils = new CoreUtils(this.w);
}
// http://stackoverflow.com/questions/326679/choosing-an-attractive-linear-scale-for-a-graphs-y-axis
// This routine creates the Y axis values for a graph.
/**
* @param {number} yMin
* @param {number} yMax
*/
niceScale(yMin, yMax, index = 0) {
const jsPrecision = 1e-11;
const w = this.w;
const gl = w.globals;
let axisCnf;
let maxTicks;
let gotMin;
let gotMax;
if (gl.isBarHorizontal) {
axisCnf = w.config.xaxis;
maxTicks = Math.max((gl.svgWidth - 100) / 25, 2);
} else {
axisCnf = w.config.yaxis[index];
maxTicks = Math.max((gl.svgHeight - 100) / 15, 2);
}
if (!Utils$1.isNumber(maxTicks)) {
maxTicks = 10;
}
gotMin = axisCnf.min !== void 0 && axisCnf.min !== null;
gotMax = axisCnf.max !== void 0 && axisCnf.min !== null;
let gotStepSize = axisCnf.stepSize !== void 0 && axisCnf.stepSize !== null;
let gotTickAmount = axisCnf.tickAmount !== void 0 && axisCnf.tickAmount !== null;
let ticks = gotTickAmount ? axisCnf.tickAmount : NICE_SCALE_DEFAULT_TICKS[Math.min(
Math.round(maxTicks / 2),
NICE_SCALE_DEFAULT_TICKS.length - 1
)];
if (gl.isMultipleYAxis && !gotTickAmount && gl.multiAxisTickAmount > 0) {
ticks = gl.multiAxisTickAmount;
gotTickAmount = true;
}
if (ticks === "dataPoints") {
ticks = gl.dataPoints - 1;
} else {
ticks = Math.abs(Math.round(ticks));
}
if (yMin === Number.MIN_VALUE && yMax === 0 || !Utils$1.isNumber(yMin) && !Utils$1.isNumber(yMax) || yMin === Number.MIN_VALUE && yMax === -Number.MAX_VALUE) {
yMin = Utils$1.isNumber(axisCnf.min) ? axisCnf.min : 0;
yMax = Utils$1.isNumber(axisCnf.max) ? axisCnf.max : yMin + ticks;
gl.allSeriesCollapsed = false;
}
if (yMin > yMax) {
console.warn(
"axis.min cannot be greater than axis.max: swapping min and max"
);
const temp = yMax;
yMax = yMin;
yMin = temp;
} else if (yMin === yMax) {
yMin = yMin === 0 ? 0 : yMin - 1;
yMax = yMax === 0 ? 2 : yMax + 1;
}
const result = [];
if (ticks < 1) {
ticks = 1;
}
let tiks = ticks;
let range = Math.abs(yMax - yMin);
const proximityRatio = 0.15;
if (!gotMin && yMin > 0 && yMin / range < proximityRatio) {
yMin = 0;
gotMin = true;
}
if (!gotMax && yMax < 0 && -yMax / range < proximityRatio) {
yMax = 0;
gotMax = true;
}
range = Math.abs(yMax - yMin);
let stepSize = range / tiks;
let niceStep = stepSize;
const mag = Math.floor(Math.log10(niceStep));
const magPow = Math.pow(10, mag);
let magMsd = Math.ceil(niceStep / magPow);
magMsd = NICE_SCALE_ALLOWED_MAG_MSD[gl.yValueDecimal === 0 ? 0 : 1][magMsd];
niceStep = magMsd * magPow;
stepSize = niceStep;
if (gl.isBarHorizontal && axisCnf.stepSize && axisCnf.type !== "datetime") {
stepSize = axisCnf.stepSize;
gotStepSize = true;
} else if (gotStepSize) {
stepSize = axisCnf.stepSize;
}
if (gotStepSize) {
if (axisCnf.forceNiceScale) {
const stepMag = Math.floor(Math.log10(stepSize));
stepSize *= Math.pow(10, mag - stepMag);
}
}
if (gotMin && gotMax) {
let crudeStep = range / tiks;
if (gotTickAmount) {
if (gotStepSize) {
if (Utils$1.mod(range, stepSize) != 0) {
const gcdStep = Utils$1.getGCD(stepSize, crudeStep);
if (crudeStep / gcdStep < 10) {
stepSize = gcdStep;
} else {
stepSize = crudeStep;
}
} else {
if (Utils$1.mod(stepSize, crudeStep) == 0) {
stepSize = crudeStep;
} else {
crudeStep = stepSize;
gotTickAmount = false;
}
}
} else {
stepSize = crudeStep;
}
} else {
if (gotStepSize) {
if (Utils$1.mod(range, stepSize) == 0) {
crudeStep = stepSize;
} else {
stepSize = crudeStep;
}
} else {
if (Utils$1.mod(range, stepSize) == 0) {
crudeStep = stepSize;
} else {
tiks = Math.ceil(range / stepSize);
crudeStep = range / tiks;
const gcdStep = Utils$1.getGCD(range, stepSize);
if (range / gcdStep < maxTicks) {
crudeStep = gcdStep;
}
stepSize = crudeStep;
}
}
}
tiks = Math.round(range / stepSize);
} else {
if (!gotMin && !gotMax) {
if (gl.isMultipleYAxis && gotTickAmount) {
const tMin = stepSize * Math.floor(yMin / stepSize);
let tMax = tMin + stepSize * tiks;
if (tMax < yMax) {
stepSize *= 2;
}
yMin = tMin;
tMax = yMax;
yMax = yMin + stepSize * tiks;
range = Math.abs(yMax - yMin);
if (yMin > 0 && yMin < Math.abs(tMax - yMax)) {
yMin = 0;
yMax = stepSize * tiks;
}
if (yMax < 0 && -yMax < Math.abs(tMin - yMin)) {
yMax = 0;
yMin = -stepSize * tiks;
}
} else {
yMin = stepSize * Math.floor(yMin / stepSize);
yMax = stepSize * Math.ceil(yMax / stepSize);
}
} else if (gotMax) {
if (gotTickAmount) {
yMin = yMax - stepSize * tiks;
} else {
const yMinPrev = yMin;
yMin = stepSize * Math.floor(yMin / stepSize);
if (Math.abs(yMax - yMin) / Utils$1.getGCD(range, stepSize) > maxTicks) {
yMin = yMax - stepSize * ticks;
yMin += stepSize * Math.floor((yMinPrev - yMin) / stepSize);
}
}
} else if (gotMin) {
if (gotTickAmount) {
yMax = yMin + stepSize * tiks;
} else {
const yMaxPrev = yMax;
yMax = stepSize * Math.ceil(yMax / stepSize);
if (Math.abs(yMax - yMin) / Utils$1.getGCD(range, stepSize) > maxTicks) {
yMax = yMin + stepSize * ticks;
yMax += stepSize * Math.ceil((yMaxPrev - yMax) / stepSize);
}
}
}
range = Math.abs(yMax - yMin);
stepSize = Utils$1.getGCD(range, stepSize);
tiks = Math.round(range / stepSize);
}
if (!gotTickAmount && !(gotMin || gotMax)) {
tiks = Math.ceil((range - jsPrecision) / (stepSize + jsPrecision));
if (tiks > 16 && Utils$1.getPrimeFactors(tiks).length < 2) {
tiks++;
}
}
if (!gotTickAmount && axisCnf.forceNiceScale && gl.yValueDecimal === 0 && tiks > range) {
tiks = range;
stepSize = Math.round(range / tiks);
}
if (tiks > maxTicks && (!(gotTickAmount || gotStepSize) || axisCnf.forceNiceScale)) {
const pf = Utils$1.getPrimeFactors(tiks);
const last = pf.length - 1;
let tt = tiks;
reduceLoop: for (var xFactors = 0; xFactors < last; xFactors++) {
for (var lowest = 0; lowest <= last - xFactors; lowest++) {
const stop = Math.min(lowest + xFactors, last);
let t = tt;
let div = 1;
for (var next = lowest; next <= stop; next++) {
div *= pf[next];
}
t /= div;
if (t < maxTicks) {
tt = t;
break reduceLoop;
}
}
}
if (tt === tiks) {
stepSize = range;
} else {
stepSize = range / tt;
}
tiks = Math.round(range / stepSize);
}
if (gl.isMultipleYAxis && gl.multiAxisTickAmount == 0 && gl.ignoreYAxisIndexes.indexOf(index) < 0) {
gl.multiAxisTickAmount = tiks;
}
let val = yMin - stepSize;
const err = stepSize * jsPrecision;
do {
val += stepSize;
result.push(Utils$1.stripNumber(val, 7));
} while (yMax - val > err);
return {
result,
niceMin: result[0],
niceMax: result[result.length - 1]
};
}
/** @param {number} yMin @param {number} yMax @param {number|string} ticks @param {number} index @param {number|undefined} step */
linearScale(yMin, yMax, ticks = 10, index = 0, step = void 0) {
const range = Math.abs(yMax - yMin);
let result = [];
if (yMin === yMax) {
result = [yMin];
return {
result,
niceMin: result[0],
niceMax: result[result.length - 1]
};
}
ticks = this._adjustTicksForSmallRange(ticks, index, range);
if (
/** @type {any} */
ticks === "dataPoints"
) {
ticks = this.w.globals.dataPoints - 1;
}
const ticksNum = (
/** @type {number} */
ticks
);
if (!step) {
step = range / ticksNum;
}
const MIN_PRECISION = 2;
if (step !== 0 && isFinite(step)) {
const magnitude = Math.floor(Math.log10(Math.abs(step)));
const precision = Math.max(MIN_PRECISION, -magnitude + MIN_PRECISION);
const multiplier = Math.pow(10, precision);
step = Math.round((step + Number.EPSILON) * multiplier) / multiplier;
}
let tickCount = ticks === Number.MAX_VALUE ? 5 : ticksNum;
if (ticks === Number.MAX_VALUE) {
step = 1;
}
let v = yMin;
while (tickCount >= 0) {
result.push(v);
v = Utils$1.preciseAddition(v, step);
tickCount -= 1;
}
return {
result,
niceMin: result[0],
niceMax: result[result.length - 1]
};
}
/**
* @param {number} yMin
* @param {number} yMax
* @param {number} base
*/
logarithmicScaleNice(yMin, yMax, base) {
if (yMax <= 0) yMax = Math.max(yMin, base);
if (yMin <= 0) yMin = Math.min(yMax, base);
const logs = [];
const logMax = Math.ceil(Math.log(yMax) / Math.log(base) + 1);
const logMin = Math.floor(Math.log(yMin) / Math.log(base));
for (let i = logMin; i < logMax; i++) {
logs.push(Math.pow(base, i));
}
return {
result: logs,
niceMin: logs[0],
niceMax: logs[logs.length - 1]
};
}
/**
* @param {number} yMin
* @param {number} yMax
* @param {number} base
*/
logarithmicScale(yMin, yMax, base) {
if (yMax <= 0) yMax = Math.max(yMin, base);
if (yMin <= 0) yMin = Math.min(yMax, base);
const logs = [];
const logMax = Math.log(yMax) / Math.log(base);
const logMin = Math.log(yMin) / Math.log(base);
const logRange = logMax - logMin;
const ticks = Math.round(logRange);
const logTickSpacing = logRange / ticks;
for (let i = 0, logTick = logMin; i < ticks; i++, logTick += logTickSpacing) {
logs.push(Math.pow(base, logTick));
}
logs.push(Math.pow(base, logMax));
return {
result: logs,
niceMin: yMin,
niceMax: yMax
};
}
/**
* @param {number | string} ticks
* @param {number} index
* @param {number} range
*/
_adjustTicksForSmallRange(ticks, index, range) {
let newTicks = ticks;
if (typeof index !== "undefined" && this.w.config.yaxis[index].labels.formatter && this.w.config.yaxis[index].tickAmount === void 0) {
const formattedVal = Number(
this.w.config.yaxis[index].labels.formatter(1)
);
if (Utils$1.isNumber(formattedVal) && this.w.globals.yValueDecimal === 0) {
newTicks = Math.ceil(range);
}
}
return newTicks < ticks ? newTicks : ticks;
}
/**
* @param {number} index
* @param {number} minY
* @param {number} maxY
*/
setYScaleForIndex(index, minY, maxY) {
const gl = this.w.globals;
const cnf = this.w.config;
const y = gl.isBarHorizontal ? cnf.xaxis : cnf.yaxis[index];
if (typeof gl.yAxisScale[index] === "undefined") {
gl.yAxisScale[index] = [];
}
const range = Math.abs(maxY - minY);
if (y.logarithmic && range <= 5) {
gl.invalidLogScale = true;
}
if (y.logarithmic && range > 5) {
gl.allSeriesCollapsed = false;
gl.yAxisScale[index] = y.forceNiceScale ? this.logarithmicScaleNice(minY, maxY, y.logBase) : this.logarithmicScale(minY, maxY, y.logBase);
} else {
if (maxY === -Number.MAX_VALUE || !Utils$1.isNumber(maxY) || minY === Number.MAX_VALUE || !Utils$1.isNumber(minY)) {
gl.yAxisScale[index] = this.niceScale(
Number.MIN_VALUE,
0,
index
);
} else {
gl.allSeriesCollapsed = false;
gl.yAxisScale[index] = this.niceScale(
minY,
maxY,
index
);
}
}
}
/**
* @param {number} minX
* @param {number} maxX
*/
setXScale(minX, maxX) {
const w = this.w;
const gl = w.globals;
if (maxX === -Number.MAX_VALUE || !Utils$1.isNumber(maxX)) {
gl.xAxisScale = this.linearScale(0, 10, 10);
} else {
const ticks = gl.xTickAmount;
gl.xAxisScale = this.linearScale(
minX,
maxX,
ticks,
0,
w.config.xaxis.max === void 0 ? w.config.xaxis.stepSize : void 0
);
}
return gl.xAxisScale;
}
scaleMultipleYAxes() {
const cnf = this.w.config;
const gl = this.w.globals;
this.coreUtils.setSeriesYAxisMappings();
const axisSeriesMap = gl.seriesYAxisMap;
const minYArr = gl.minYArr;
const maxYArr = gl.maxYArr;
gl.allSeriesCollapsed = true;
gl.barGroups = [];
axisSeriesMap.forEach((axisSeries, ai) => {
const groupNames = [];
axisSeries.forEach((as) => {
var _a;
const group = (
/** @type {Record<string,any>} */
(_a = cnf.series[as]) == null ? void 0 : _a.group
);
if (groupNames.indexOf(group) < 0) {
groupNames.push(group);
}
});
if (axisSeries.length > 0) {
let minY = Number.MAX_VALUE;
let maxY = -Number.MAX_VALUE;
let lowestY = minY;
let highestY = maxY;
let seriesType;
let seriesGroupName;
if (cnf.chart.stacked) {
const mapSeries = new Array(gl.dataPoints).fill(0);
const sumSeries = [];
const posSeries = [];
const negSeries = [];
groupNames.forEach(() => {
sumSeries.push(mapSeries.map(() => Number.MIN_VALUE));
posSeries.push(mapSeries.map(() => Number.MIN_VALUE));
negSeries.push(mapSeries.map(() => Number.MIN_VALUE));
});
for (let i = 0; i < axisSeries.length; i++) {
if (!seriesType && /** @type {Record<string,any>} */
cnf.series[axisSeries[i]].type) {
seriesType = /** @type {Record<string,any>} */
cnf.series[axisSeries[i]].type;
}
const si = axisSeries[i];
if (
/** @type {Record<string,any>} */
cnf.series[si].group
) {
seriesGroupName = /** @type {Record<string,any>} */
cnf.series[si].group;
} else {
seriesGroupName = "axis-".concat(ai.toString());
}
const collapsed = !(gl.collapsedSeriesIndices.indexOf(si) < 0 && gl.ancillaryCollapsedSeriesIndices.indexOf(si) < 0);
if (!collapsed) {
gl.allSeriesCollapsed = false;
groupNames.forEach((gn, gni) => {
if (
/** @type {Record<string,any>} */
cnf.series[si].group === gn
) {
for (let j = 0; j < this.w.seriesData.series[si].length; j++) {
const val = this.w.seriesData.series[si][j];
if (val >= 0) {
posSeries[gni][j] += val;
} else {
negSeries[gni][j] += val;
}
sumSeries[gni][j] += val;
lowestY = Math.min(lowestY, val);
highestY = Math.max(highestY, val);
}
}
});
}
if (seriesType === "bar" || seriesType === "column") {
gl.barGroups.push(seriesGroupName);
}
}
if (!seriesType) {
seriesType = cnf.chart.type;
}
if (seriesType === "bar" || seriesType === "column") {
groupNames.forEach((gn, gni) => {
minY = Math.min(minY, Math.min.apply(null, negSeries[gni]));
maxY = Math.max(maxY, Math.max.apply(null, posSeries[gni]));
});
} else {
groupNames.forEach((gn, gni) => {
lowestY = Math.min(lowestY, Math.min.apply(null, sumSeries[gni]));
highestY = Math.max(
highestY,
Math.max.apply(null, sumSeries[gni])
);
});
minY = lowestY;
maxY = highestY;
}
if (minY === Number.MIN_VALUE && maxY === Number.MIN_VALUE) {
maxY = -Number.MAX_VALUE;
}
} else {
for (let i = 0; i < axisSeries.length; i++) {
const si = axisSeries[i];
minY = Math.min(minY, minYArr[si]);
maxY = Math.max(maxY, maxYArr[si]);
const collapsed = !(gl.collapsedSeriesIndices.indexOf(si) < 0 && gl.ancillaryCollapsedSeriesIndices.indexOf(si) < 0);
if (!collapsed) {
gl.allSeriesCollapsed = false;
}
}
}
if (cnf.yaxis[ai].min !== void 0) {
if (typeof cnf.yaxis[ai].min === "function") {
minY = cnf.yaxis[ai].min(minY);
} else {
minY = cnf.yaxis[ai].min;
}
}
if (cnf.yaxis[ai].max !== void 0) {
if (typeof cnf.yaxis[ai].max === "function") {
maxY = cnf.yaxis[ai].max(maxY);
} else {
maxY = cnf.yaxis[ai].max;
}
}
gl.barGroups = gl.barGroups.filter((v, i, a) => a.indexOf(v) === i);
this.setYScaleForIndex(ai, minY, maxY);
axisSeries.forEach((si) => {
minYArr[si] = gl.yAxisScale[ai].niceMin;
maxYArr[si] = gl.yAxisScale[ai].niceMax;
});
} else {
this.setYScaleForIndex(ai, 0, -Number.MAX_VALUE);
}
});
}
}
class Range {
/**
* @param {import('../types/internal').ChartStateW} w
*/
constructor(w) {
this.w = w;
this.scales = new Scales(this.w);
}
init() {
this.setYRange();
this.setXRange();
this.setZRange();
}
/**
* @param {number} startingSeriesIndex
* @param {number | null} [endingSeriesIndex]
*/
getMinYMaxY(startingSeriesIndex, lowestY = Number.MAX_VALUE, highestY = -Number.MAX_VALUE, endingSeriesIndex = null) {
var _a, _b, _c, _d, _e;
const cnf = this.w.config;
const gl = this.w.globals;
let maxY = -Number.MAX_VALUE;
let minY = Number.MIN_VALUE;
if (endingSeriesIndex === null) {
endingSeriesIndex = startingSeriesIndex + 1;
}
const series = this.w.seriesData.series;
let seriesMin = series;
let seriesMax = series;
if (cnf.chart.type === "candlestick") {
seriesMin = this.w.candleData.seriesCandleL;
seriesMax = this.w.candleData.seriesCandleH;
} else if (cnf.chart.type === "boxPlot") {
seriesMin = this.w.candleData.seriesCandleO;
seriesMax = this.w.candleData.seriesCandleC;
} else if (this.w.axisFlags.isRangeData) {
seriesMin = this.w.rangeData.seriesRangeStart;
seriesMax = this.w.rangeData.seriesRangeEnd;
}
let autoScaleYaxis = false;
if (this.w.seriesData.seriesX.length >= endingSeriesIndex) {
const brush = (
/** @type {any} */
(_a = gl.brushSource) == null ? void 0 : _a.w.config.chart.brush
);
if (cnf.chart.zoom.enabled && cnf.chart.zoom.autoScaleYaxis || (brush == null ? void 0 : brush.enabled) && (brush == null ? void 0 : brush.autoScaleYaxis)) {
autoScaleYaxis = true;
}
}
for (let i = startingSeriesIndex; i < endingSeriesIndex; i++) {
gl.dataPoints = Math.max(gl.dataPoints, series[i].length);
const seriesType = (
/** @type {Record<string,any>} */
cnf.series[i].type
);
if (this.w.labelData.categoryLabels.length) {
gl.dataPoints = this.w.labelData.categoryLabels.filter(
(label) => typeof label !== "undefined"
).length;
}
if (this.w.labelData.labels.length && cnf.xaxis.type !== "datetime" && /**
* @param {number} a
* @param {number[]} c
*/
this.w.seriesData.series.reduce((a, c) => a + c.length, 0) !== 0) {
gl.dataPoints = Math.max(gl.dataPoints, this.w.labelData.labels.length);
}
let firstXIndex = 0;
let lastXIndex = series[i].length - 1;
if (autoScaleYaxis) {
if (cnf.xaxis.min) {
for (; firstXIndex < lastXIndex && this.w.seriesData.seriesX[i][firstXIndex] < cnf.xaxis.min; firstXIndex++) {
}
}
if (cnf.xaxis.max) {
for (; lastXIndex > firstXIndex && this.w.seriesData.seriesX[i][lastXIndex] > cnf.xaxis.max; lastXIndex--) {
}
}
}
for (let j = firstXIndex; j <= lastXIndex && j < this.w.seriesData.series[i].length; j++) {
let val = series[i][j];
if (val !== null && Utils$1.isNumber(val)) {
if (typeof ((_b = seriesMax[i]) == null ? void 0 : _b[j]) !== "undefined") {
maxY = Math.max(maxY, seriesMax[i][j]);
lowestY = Math.min(lowestY, seriesMax[i][j]);
}
if (typeof ((_c = seriesMin[i]) == null ? void 0 : _c[j]) !== "undefined") {
lowestY = Math.min(lowestY, seriesMin[i][j]);
highestY = Math.max(highestY, seriesMin[i][j]);
}
switch (seriesType) {
case "candlestick":
{
if (typeof this.w.candleData.seriesCandleC[i][j] !== "undefined") {
maxY = Math.max(maxY, this.w.candleData.seriesCandleH[i][j]);
lowestY = Math.min(
lowestY,
this.w.candleData.seriesCandleL[i][j]
);
}
}
break;
case "boxPlot":
{
if (typeof this.w.candleData.seriesCandleC[i][j] !== "undefined") {
maxY = Math.max(maxY, this.w.candleData.seriesCandleC[i][j]);
lowestY = Math.min(
lowestY,
this.w.candleData.seriesCandleO[i][j]
);
}
}
break;
}
if (seriesType && seriesType !== "candlestick" && seriesType !== "boxPlot" && seriesType !== "rangeArea" && seriesType !== "rangeBar") {
maxY = Math.max(maxY, this.w.seriesData.series[i][j]);
lowestY = Math.min(lowestY, this.w.seriesData.series[i][j]);
}
if (this.w.seriesData.seriesGoals[i] && this.w.seriesData.seriesGoals[i][j] && Array.isArray(this.w.seriesData.seriesGoals[i][j])) {
this.w.seriesData.seriesGoals[i][j].forEach(
(g) => {
maxY = Math.max(maxY, g.value);
lowestY = Math.min(lowestY, g.value);
}
);
}
highestY = maxY;
val = Utils$1.noExponents(val);
if (Utils$1.isFloat(val)) {
gl.yValueDecimal = Math.max(
gl.yValueDecimal,
val.toString().split(".")[1].length
);
}
if (minY > ((_d = seriesMin[i]) == null ? void 0 : _d[j]) && ((_e = seriesMin[i]) == null ? void 0 : _e[j]) < 0) {
minY = seriesMin[i][j];
}
} else {
gl.hasNullValues = true;
}
}
if (seriesType === "bar" || seriesType === "column") {
if (minY < 0 && maxY < 0) {
maxY = 0;
highestY = Math.max(highestY, 0);
}
if (minY === Number.MIN_VALUE) {
minY = 0;
lowestY = Math.min(lowestY, 0);
}
}
}
if (cnf.chart.type === "rangeBar" && this.w.rangeData.seriesRangeStart.length && gl.isBarHorizontal) {
minY = lowestY;
}
if (cnf.chart.type === "bar") {
if (minY < 0 && maxY < 0) {
maxY = 0;
}
if (minY === Number.MIN_VALUE) {
minY = 0;
}
}
return {
minY,
maxY,
lowestY,
highestY
};
}
setYRange() {
const gl = this.w.globals;
const cnf = this.w.config;
gl.maxY = -Number.MAX_VALUE;
gl.minY = Number.MIN_VALUE;
let lowestYInAllSeries = Number.MAX_VALUE;
let minYMaxY;
if (gl.isMultipleYAxis) {
lowestYInAllSeries = Number.MAX_VALUE;
for (let i = 0; i < this.w.seriesData.series.length; i++) {
minYMaxY = this.getMinYMaxY(i);
gl.minYArr[i] = minYMaxY.lowestY;
gl.maxYArr[i] = minYMaxY.highestY;
lowestYInAllSeries = Math.min(lowestYInAllSeries, minYMaxY.lowestY);
}
}
minYMaxY = this.getMinYMaxY(
0,
lowestYInAllSeries,
void 0,
this.w.seriesData.series.length
);
if (cnf.chart.type === "bar") {
gl.minY = minYMaxY.minY;
gl.maxY = minYMaxY.maxY;
} else {
gl.minY = minYMaxY.lowestY;
gl.maxY = minYMaxY.highestY;
}
lowestYInAllSeries = minYMaxY.lowestY;
if (cnf.chart.stacked) {
this._setStackedMinMax();
}
if (cnf.chart.type === "line" || cnf.chart.type === "area" || cnf.chart.type === "scatter" || cnf.chart.type === "candlestick" || cnf.chart.type === "boxPlot" || cnf.chart.type === "rangeBar" && !gl.isBarHorizontal) {
if (gl.minY === Number.MIN_VALUE && lowestYInAllSeries !== -Number.MAX_VALUE && lowestYInAllSeries !== gl.maxY) {
gl.minY = lowestYInAllSeries;
}
} else {
gl.minY = gl.minY !== Number.MIN_VALUE ? Math.min(minYMaxY.minY, gl.minY) : minYMaxY.minY;
}
cnf.yaxis.forEach((yaxe, index) => {
if (yaxe.max !== void 0) {
if (typeof yaxe.max === "number") {
gl.maxYArr[index] = yaxe.max;
} else if (typeof yaxe.max === "function") {
gl.maxYArr[index] = yaxe.max(
gl.isMultipleYAxis ? gl.maxYArr[index] : gl.maxY
);
}
gl.maxY = gl.maxYArr[index];
}
if (yaxe.min !== void 0) {
if (typeof yaxe.min === "number") {
gl.minYArr[index] = yaxe.min;
} else if (typeof yaxe.min === "function") {
gl.minYArr[index] = yaxe.min(
gl.isMultipleYAxis ? gl.minYArr[index] === Number.MIN_VALUE ? 0 : gl.minYArr[index] : gl.minY
);
}
gl.minY = gl.minYArr[index];
}
});
if (gl.isBarHorizontal) {
const minmax = ["min", "max"];
minmax.forEach((m) => {
if (cnf.xaxis[m] !== void 0 && typeof cnf.xaxis[m] === "number") {
m === "min" ? gl.minY = cnf.xaxis[m] : gl.maxY = cnf.xaxis[m];
}
});
}
if (gl.isMultipleYAxis) {
this.scales.scaleMultipleYAxes();
gl.minY = lowestYInAllSeries;
} else {
this.scales.setYScaleForIndex(0, gl.minY, gl.maxY);
gl.minY = gl.yAxisScale[0].niceMin;
gl.maxY = gl.yAxisScale[0].niceMax;
gl.minYArr[0] = gl.minY;
gl.maxYArr[0] = gl.maxY;
}
gl.barGroups = [];
gl.lineGroups = [];
gl.areaGroups = [];
cnf.series.forEach((s) => {
const _s = (
/** @type {any} */
s
);
const type = _s.type || cnf.chart.type;
switch (type) {
case "bar":
case "column":
gl.barGroups.push(_s.group);
break;
case "line":
gl.lineGroups.push(_s.group);
break;
case "area":
gl.areaGroups.push(_s.group);
break;
}
});
gl.barGroups = gl.barGroups.filter((v, i, a) => a.indexOf(v) === i);
gl.lineGroups = gl.lineGroups.filter((v, i, a) => a.indexOf(v) === i);
gl.areaGroups = gl.areaGroups.filter((v, i, a) => a.indexOf(v) === i);
return {
minY: gl.minY,
maxY: gl.maxY,
minYArr: gl.minYArr,
maxYArr: gl.maxYArr,
yAxisScale: gl.yAxisScale
};
}
setXRange() {
const gl = this.w.globals;
const cnf = this.w.config;
const isXNumeric = cnf.xaxis.type === "numeric" || cnf.xaxis.type === "datetime" || cnf.xaxis.type === "category" && !this.w.axisFlags.noLabelsProvided || this.w.axisFlags.noLabelsProvided || this.w.axisFlags.isXNumeric;
const getInitialMinXMaxX = () => {
for (let i = 0; i < this.w.seriesData.series.length; i++) {
if (this.w.labelData.labels[i]) {
for (let j = 0; j < this.w.labelData.labels[i].length; j++) {
if (this.w.labelData.labels[i][j] !== null && Utils$1.isNumber(this.w.labelData.labels[i][j])) {
gl.maxX = Math.max(
gl.maxX,
/** @type {number} */
/** @type {any} */
this.w.labelData.labels[i][j]
);
gl.initialMaxX = Math.max(
gl.maxX,
/** @type {number} */
/** @type {any} */
this.w.labelData.labels[i][j]
);
gl.minX = Math.min(
gl.minX,
/** @type {number} */
/** @type {any} */
this.w.labelData.labels[i][j]
);
gl.initialMinX = Math.min(
gl.minX,
/** @type {number} */
/** @type {any} */
this.w.labelData.labels[i][j]
);
}
}
}
}
};
if (this.w.axisFlags.isXNumeric) {
getInitialMinXMaxX();
}
if (this.w.axisFlags.noLabelsProvided) {
if (cnf.xaxis.categories.length === 0) {
gl.maxX = /** @type {any} */
this.w.labelData.labels[this.w.labelData.labels.length - 1];
gl.initialMaxX = /** @type {any} */
this.w.labelData.labels[this.w.labelData.labels.length - 1];
gl.minX = 1;
gl.initialMinX = 1;
}
}
if (this.w.axisFlags.isXNumeric || this.w.axisFlags.noLabelsProvided || this.w.axisFlags.dataFormatXNumeric) {
let ticks = 10;
if (cnf.xaxis.tickAmount === void 0) {
ticks = Math.round(gl.svgWidth / 150);
if (cnf.xaxis.type === "numeric" && gl.dataPoints < 30) {
ticks = gl.dataPoints - 1;
}
if (ticks > gl.dataPoints && gl.dataPoints !== 0) {
ticks = gl.dataPoints - 1;
}
} else if (cnf.xaxis.tickAmount === "dataPoints") {
if (this.w.seriesData.series.length > 1) {
ticks = this.w.seriesData.series[gl.maxValsInArrayIndex].length - 1;
}
if (this.w.axisFlags.isXNumeric) {
const diff = Math.round(gl.maxX - gl.minX);
if (diff < 30) {
ticks = diff;
}
}
} else {
ticks = cnf.xaxis.tickAmount;
}
gl.xTickAmount = ticks;
if (cnf.xaxis.max !== void 0 && typeof cnf.xaxis.max === "number") {
gl.maxX = cnf.xaxis.max;
}
if (cnf.xaxis.min !== void 0 && typeof cnf.xaxis.min === "number") {
gl.minX = cnf.xaxis.min;
}
if (cnf.xaxis.range !== void 0) {
gl.minX = gl.maxX - cnf.xaxis.range;
}
if (gl.minX !== Number.MAX_VALUE && gl.maxX !== -Number.MAX_VALUE) {
if (cnf.xaxis.convertedCatToNumeric && !this.w.axisFlags.dataFormatXNumeric) {
const catScale = [];
for (let i = gl.minX - 1; i < gl.maxX; i++) {
catScale.push(i + 1);
}
gl.xAxisScale = {
result: catScale,
niceMin: catScale[0],
niceMax: catScale[catScale.length - 1]
};
} else {
gl.xAxisScale = this.scales.setXScale(gl.minX, gl.maxX);
}
} else {
gl.xAxisScale = this.scales.linearScale(
0,
ticks,
ticks,
0,
cnf.xaxis.stepSize
);
if (this.w.axisFlags.noLabelsProvided && this.w.labelData.labels.length > 0) {
gl.xAxisScale = this.scales.linearScale(
1,
this.w.labelData.labels.length,
ticks - 1,
0,
cnf.xaxis.stepSize
);
this.w.seriesData.seriesX = /** @type {any} */
this.w.labelData.labels.slice();
}
}
if (isXNumeric) {
this.w.labelData.labels = /** @type {any} */
gl.xAxisScale.result.slice();
}
}
if (gl.isBarHorizontal && this.w.labelData.labels.length) {
gl.xTickAmount = this.w.labelData.labels.length;
}
this._handleSingleDataPoint();
this._getMinXDiff();
return {
minX: gl.minX,
maxX: gl.maxX
};
}
setZRange() {
const gl = this.w.globals;
if (!this.w.axisFlags.isDataXYZ) return;
for (let i = 0; i < this.w.seriesData.series.length; i++) {
if (typeof this.w.seriesData.seriesZ[i] !== "undefined") {
for (let j = 0; j < this.w.seriesData.seriesZ[i].length; j++) {
if (this.w.seriesData.seriesZ[i][j] !== null && Utils$1.isNumber(this.w.seriesData.seriesZ[i][j])) {
gl.maxZ = Math.max(gl.maxZ, this.w.seriesData.seriesZ[i][j]);
gl.minZ = Math.min(gl.minZ, this.w.seriesData.seriesZ[i][j]);
}
}
}
}
}
_handleSingleDataPoint() {
const gl = this.w.globals;
const cnf = this.w.config;
if (gl.minX === gl.maxX) {
const datetimeObj = new DateTime(this.w);
if (cnf.xaxis.type === "datetime") {
const newMinX = datetimeObj.getDate(gl.minX);
if (cnf.xaxis.labels.datetimeUTC) {
newMinX.setUTCDate(newMinX.getUTCDate() - 2);
} else {
newMinX.setDate(newMinX.getDate() - 2);
}
gl.minX = new Date(newMinX).getTime();
const newMaxX = datetimeObj.getDate(gl.maxX);
if (cnf.xaxis.labels.datetimeUTC) {
newMaxX.setUTCDate(newMaxX.getUTCDate() + 2);
} else {
newMaxX.setDate(newMaxX.getDate() + 2);
}
gl.maxX = new Date(newMaxX).getTime();
} else if (cnf.xaxis.type === "numeric" || cnf.xaxis.type === "category" && !this.w.axisFlags.noLabelsProvided) {
gl.minX = gl.minX - 2;
gl.initialMinX = gl.minX;
gl.maxX = gl.maxX + 2;
gl.initialMaxX = gl.maxX;
}
}
}
_getMinXDiff() {
const gl = this.w.globals;
if (this.w.axisFlags.isXNumeric) {
this.w.seriesData.seriesX.forEach((sX) => {
if (sX.length) {
if (sX.length === 1) {
sX.push(
this.w.seriesData.seriesX[gl.maxValsInArrayIndex][this.w.seriesData.seriesX[gl.maxValsInArrayIndex].length - 1]
);
}
const seriesX = sX.slice();
seriesX.sort((a, b) => a - b);
seriesX.forEach((s, j) => {
if (j > 0) {
const xDiff = s - seriesX[j - 1];
if (xDiff > 0) {
gl.minXDiff = Math.min(xDiff, gl.minXDiff);
}
}
});
if (gl.dataPoints === 1 || gl.minXDiff === Number.MAX_VALUE) {
gl.minXDiff = 0.5;
}
}
});
}
}
_setStackedMinMax() {
const gl = this.w.globals;
if (!this.w.seriesData.series.length) return;
let seriesGroups = this.w.labelData.seriesGroups;
if (!seriesGroups.length) {
seriesGroups = [this.w.seriesData.seriesNames.map((name2) => name2)];
}
const stackedPoss = {};
const stackedNegs = {};
seriesGroups.forEach((group) => {
stackedPoss[group] = [];
stackedNegs[group] = [];
const indicesOfSeriesInGroup = this.w.config.series.map(
(serie, si) => group.indexOf(this.w.seriesData.seriesNames[si]) > -1 ? si : null
).filter((f) => f !== null);
indicesOfSeriesInGroup.forEach((i) => {
var _a, _b, _c, _d;
for (let j = 0; j < this.w.seriesData.series[gl.maxValsInArrayIndex].length; j++) {
if (typeof stackedPoss[group][j] === "undefined") {
stackedPoss[group][j] = 0;
stackedNegs[group][j] = 0;
}
const stackSeries = this.w.config.chart.stacked && !gl.comboCharts || this.w.config.chart.stacked && gl.comboCharts && (!this.w.config.chart.stackOnlyBar || /** @type {Record<string,any>} */
((_b = (_a = this.w.config.series) == null ? void 0 : _a[i]) == null ? void 0 : _b.type) === "bar" || /** @type {Record<string,any>} */
((_d = (_c = this.w.config.series) == null ? void 0 : _c[i]) == null ? void 0 : _d.type) === "column");
if (stackSeries) {
if (this.w.seriesData.series[i][j] !== null && Utils$1.isNumber(this.w.seriesData.series[i][j])) {
this.w.seriesData.series[i][j] > 0 ? stackedPoss[group][j] += parseFloat(String(this.w.seriesData.series[i][j])) + 1e-4 : stackedNegs[group][j] += parseFloat(
String(this.w.seriesData.series[i][j])
);
}
}
}
});
});
Object.entries(stackedPoss).forEach(([key]) => {
stackedPoss[key].forEach(
(_, stgi) => {
gl.maxY = Math.max(gl.maxY, stackedPoss[key][stgi]);
gl.minY = Math.min(gl.minY, stackedNegs[key][stgi]);
}
);
});
}
}
function getThemePalettes() {
return {
palette1: ["#008FFB", "#00E396", "#FEB019", "#FF4560", "#775DD0"],
palette2: ["#3F51B5", "#03A9F4", "#4CAF50", "#F9CE1D", "#FF9800"],
palette3: ["#33B2DF", "#546E7A", "#D4526E", "#13D8AA", "#A5978B"],
palette4: ["#4ECDC4", "#C7F464", "#81D4FA", "#FD6A6A", "#546E7A"],
palette5: ["#2B908F", "#F9A3A4", "#90EE7E", "#FA4443", "#69D2E7"],
palette6: ["#449DD1", "#F86624", "#EA3546", "#662E9B", "#C5D86D"],
palette7: ["#D7263D", "#1B998B", "#2E294E", "#F46036", "#E2C044"],
palette8: ["#662E9B", "#F86624", "#F9C80E", "#EA3546", "#43BCCD"],
palette9: ["#5C4742", "#A5978B", "#8D5B4C", "#5A2A27", "#C4BBAF"],
palette10: ["#A300D6", "#7D02EB", "#5653FE", "#2983FF", "#00B1F2"],
// CVD-safe palettes (Wong 2011 / IBM design)
cvdDeuteranopia: ["#0072B2", "#E69F00", "#56B4E9", "#009E73", "#F0E442", "#D55E00", "#CC79A7"],
cvdProtanopia: ["#0077BB", "#EE7733", "#009988", "#EE3377", "#BBBBBB", "#33BBEE", "#CC3311"],
cvdTritanopia: ["#CC3311", "#009988", "#EE7733", "#0077BB", "#EE3377", "#BBBBBB", "#33BBEE"],
highContrast: ["#005A9C", "#C00000", "#007A33", "#6C3483", "#7B3F00", "#0097A7", "#4A235A"]
};
}
class YAxis {
/**
* @param {import('../../types/internal').ChartStateW} w
* @param {any} [elgrid]
*/
constructor(w, { theme = null, timeScale = null } = {}, elgrid) {
this.w = w;
this.elgrid = elgrid;
this.xaxisFontSize = w.config.xaxis.labels.style.fontSize;
this.axisFontFamily = w.config.xaxis.labels.style.fontFamily;
this.xaxisForeColors = w.config.xaxis.labels.style.colors;
this.isCategoryBarHorizontal = w.config.chart.type === "bar" && w.config.plotOptions.bar.horizontal;
this.xAxisoffX = w.config.xaxis.position === "bottom" ? w.layout.gridHeight : 0;
this.drawnLabels = [];
this.axesUtils = new AxesUtils(w, { theme, timeScale });
}
/**
* @param {number} realIndex
*/
drawYaxis(realIndex) {
const w = this.w;
const graphics = new Graphics(this.w);
const yaxisStyle = w.config.yaxis[realIndex].labels.style;
const {
fontSize: yaxisFontSize,
fontFamily: yaxisFontFamily,
fontWeight: yaxisFontWeight
} = yaxisStyle;
const elYaxis = graphics.group({
class: "apexcharts-yaxis",
rel: realIndex,
transform: `translate(${w.globals.translateYAxisX[realIndex]}, 0)`
});
if (this.axesUtils.isYAxisHidden(realIndex)) return elYaxis;
const elYaxisTexts = graphics.group({ class: "apexcharts-yaxis-texts-g" });
elYaxis.add(elYaxisTexts);
const tickAmount = w.globals.yAxisScale[realIndex].result.length - 1;
const labelsDivider = w.layout.gridHeight / tickAmount;
const lbFormatter = w.formatters.yLabelFormatters[realIndex];
const labels = this.axesUtils.checkForReversedLabels(
realIndex,
w.globals.yAxisScale[realIndex].result.slice()
);
if (w.config.yaxis[realIndex].labels.show) {
let lY = w.layout.translateY + w.config.yaxis[realIndex].labels.offsetY;
if (w.globals.isBarHorizontal) lY = 0;
else if (w.config.chart.type === "heatmap") lY -= labelsDivider / 2;
lY += parseInt(yaxisFontSize, 10) / 3;
let firstLabel = null;
for (let i = tickAmount; i >= 0; i--) {
const val = lbFormatter(labels[i], i, w);
let xPad = w.config.yaxis[realIndex].labels.padding;
if (w.config.yaxis[realIndex].opposite && w.config.yaxis.length !== 0)
xPad *= -1;
const textAnchor = this.getTextAnchor(
w.config.yaxis[realIndex].labels.align,
w.config.yaxis[realIndex].opposite
);
const yColors = this.axesUtils.getYAxisForeColor(
yaxisStyle.colors,
realIndex
);
const foreColor = Array.isArray(yColors) ? yColors[i] : yColors;
const existingYLabels = Array.from(
w.dom.baseEl.querySelectorAll(
`.apexcharts-yaxis[rel='${realIndex}'] .apexcharts-yaxis-label tspan`
)
).map((label2) => label2.textContent);
const label = graphics.drawText({
x: xPad,
y: lY,
text: existingYLabels.includes(val) && !w.config.yaxis[realIndex].labels.showDuplicates ? "" : val,
textAnchor,
fontSize: yaxisFontSize,
fontFamily: yaxisFontFamily,
fontWeight: yaxisFontWeight,
maxWidth: w.config.yaxis[realIndex].labels.maxWidth,
foreColor,
isPlainText: false,
cssClass: `apexcharts-yaxis-label ${yaxisStyle.cssClass}`
});
elYaxisTexts.add(label);
this.addTooltip(label, val);
if (firstLabel === null) {
firstLabel = label;
}
if (w.config.yaxis[realIndex].labels.rotate !== 0) {
this.rotateLabel(
graphics,
label,
firstLabel,
w.config.yaxis[realIndex].labels.rotate
);
}
lY += labelsDivider;
}
}
this.addYAxisTitle(graphics, elYaxis, realIndex);
this.addAxisBorder(graphics, elYaxis, realIndex, tickAmount, labelsDivider);
return elYaxis;
}
/**
* @param {string} align
* @param {boolean} opposite
*/
getTextAnchor(align, opposite) {
if (align === "left") return "start";
if (align === "center") return "middle";
if (align === "right") return "end";
return opposite ? "start" : "end";
}
/**
* @param {any} label
* @param {any} val
*/
addTooltip(label, val) {
const elTooltipTitle = BrowserAPIs.createElementNS(SVGNS, "title");
elTooltipTitle.textContent = Array.isArray(val) ? val.join(" ") : val;
label.node.appendChild(elTooltipTitle);
}
/**
* @param {import('../Graphics').default} graphics
* @param {any} label
* @param {any} firstLabel
* @param {number} rotate
*/
rotateLabel(graphics, label, firstLabel, rotate) {
const firstLabelCenter = graphics.rotateAroundCenter(firstLabel.node);
const labelCenter = graphics.rotateAroundCenter(label.node);
label.node.setAttribute(
"transform",
`rotate(${rotate} ${firstLabelCenter.x} ${labelCenter.y})`
);
}
/**
* @param {import('../Graphics').default} graphics
* @param {any} elYaxis
* @param {number} realIndex
*/
addYAxisTitle(graphics, elYaxis, realIndex) {
const w = this.w;
if (w.config.yaxis[realIndex].title.text !== void 0) {
const elYaxisTitle = graphics.group({ class: "apexcharts-yaxis-title" });
const x = w.config.yaxis[realIndex].opposite ? w.globals.translateYAxisX[realIndex] : 0;
const elYAxisTitleText = graphics.drawText({
x,
y: w.layout.gridHeight / 2 + w.layout.translateY + w.config.yaxis[realIndex].title.offsetY,
text: w.config.yaxis[realIndex].title.text,
textAnchor: "end",
foreColor: w.config.yaxis[realIndex].title.style.color,
fontSize: w.config.yaxis[realIndex].title.style.fontSize,
fontWeight: w.config.yaxis[realIndex].title.style.fontWeight,
fontFamily: w.config.yaxis[realIndex].title.style.fontFamily,
cssClass: `apexcharts-yaxis-title-text ${w.config.yaxis[realIndex].title.style.cssClass}`
});
elYaxisTitle.add(elYAxisTitleText);
elYaxis.add(elYaxisTitle);
}
}
/**
* @param {import('../Graphics').default} graphics
* @param {any} elYaxis
* @param {number} realIndex
* @param {number} tickAmount
* @param {number} labelsDivider
*/
addAxisBorder(graphics, elYaxis, realIndex, tickAmount, labelsDivider) {
const w = this.w;
const axisBorder = w.config.yaxis[realIndex].axisBorder;
let x = 31 + axisBorder.offsetX;
if (w.config.yaxis[realIndex].opposite) x = -31 - axisBorder.offsetX;
if (axisBorder.show) {
const elVerticalLine = graphics.drawLine(
x,
w.layout.translateY + axisBorder.offsetY - 2,
x,
w.layout.gridHeight + w.layout.translateY + axisBorder.offsetY + 2,
axisBorder.color,
0,
axisBorder.width
);
elYaxis.add(elVerticalLine);
}
if (w.config.yaxis[realIndex].axisTicks.show) {
this.axesUtils.drawYAxisTicks(
x,
tickAmount,
axisBorder,
w.config.yaxis[realIndex].axisTicks,
realIndex,
labelsDivider,
elYaxis
);
}
}
/**
* @param {number} realIndex
*/
drawYaxisInversed(realIndex) {
const w = this.w;
const graphics = new Graphics(this.w);
const elXaxis = graphics.group({
class: "apexcharts-xaxis apexcharts-yaxis-inversed"
});
const elXaxisTexts = graphics.group({
class: "apexcharts-xaxis-texts-g",
transform: `translate(${w.layout.translateXAxisX}, ${w.layout.translateXAxisY})`
});
elXaxis.add(elXaxisTexts);
let tickAmount = w.globals.yAxisScale[realIndex].result.length - 1;
const labelsDivider = w.layout.gridWidth / tickAmount + 0.1;
let l = labelsDivider + w.config.xaxis.labels.offsetX;
const lbFormatter = w.formatters.xLabelFormatter;
let labels = this.axesUtils.checkForReversedLabels(
realIndex,
w.globals.yAxisScale[realIndex].result.slice()
);
const timescaleLabels = w.labelData.timescaleLabels;
if (timescaleLabels.length > 0) {
this.xaxisLabels = timescaleLabels.slice();
labels = timescaleLabels.slice();
tickAmount = labels.length;
}
if (w.config.xaxis.labels.show) {
for (let i = timescaleLabels.length ? 0 : tickAmount; timescaleLabels.length ? i < timescaleLabels.length : i >= 0; timescaleLabels.length ? i++ : i--) {
let val = lbFormatter == null ? void 0 : lbFormatter(labels[i], i, w);
let x = w.layout.gridWidth + w.globals.padHorizontal - (l - labelsDivider + w.config.xaxis.labels.offsetX);
if (timescaleLabels.length) {
const label = this.axesUtils.getLabel(
labels,
timescaleLabels,
x,
i,
this.drawnLabels,
this.xaxisFontSize
);
x = label.x;
val = label.text;
this.drawnLabels.push(label.text);
if (i === 0 && w.globals.skipFirstTimelinelabel) val = "";
if (i === labels.length - 1 && w.globals.skipLastTimelinelabel)
val = "";
}
const elTick = graphics.drawText({
x,
y: this.xAxisoffX + w.config.xaxis.labels.offsetY + 30 - (w.config.xaxis.position === "top" ? w.layout.xAxisHeight + w.config.xaxis.axisTicks.height - 2 : 0),
text: val,
textAnchor: "middle",
foreColor: Array.isArray(this.xaxisForeColors) ? this.xaxisForeColors[realIndex] : this.xaxisForeColors,
fontSize: this.xaxisFontSize,
fontFamily: this.axisFontFamily,
fontWeight: w.config.xaxis.labels.style.fontWeight,
isPlainText: false,
cssClass: `apexcharts-xaxis-label ${w.config.xaxis.labels.style.cssClass}`
});
elXaxisTexts.add(elTick);
this.addTooltip(elTick, val);
l += labelsDivider;
}
}
this.inversedYAxisTitleText(elXaxis);
this.inversedYAxisBorder(elXaxis);
return elXaxis;
}
/**
* @param {any} parent
*/
inversedYAxisBorder(parent) {
const w = this.w;
const graphics = new Graphics(this.w);
const axisBorder = w.config.xaxis.axisBorder;
if (axisBorder.show) {
let lineCorrection = 0;
if (w.config.chart.type === "bar" && w.axisFlags.isXNumeric)
lineCorrection -= 15;
const elHorzLine = graphics.drawLine(
w.globals.padHorizontal + lineCorrection + axisBorder.offsetX,
this.xAxisoffX,
w.layout.gridWidth,
this.xAxisoffX,
axisBorder.color,
0,
axisBorder.height
);
if (this.elgrid && this.elgrid.elGridBorders && w.config.grid.show) {
this.elgrid.elGridBorders.add(elHorzLine);
} else {
parent.add(elHorzLine);
}
}
}
/**
* @param {any} parent
*/
inversedYAxisTitleText(parent) {
const w = this.w;
const graphics = new Graphics(this.w);
if (w.config.xaxis.title.text !== void 0) {
const elYaxisTitle = graphics.group({
class: "apexcharts-xaxis-title apexcharts-yaxis-title-inversed"
});
const elYAxisTitleText = graphics.drawText({
x: w.layout.gridWidth / 2 + w.config.xaxis.title.offsetX,
y: this.xAxisoffX + parseFloat(this.xaxisFontSize) + parseFloat(w.config.xaxis.title.style.fontSize) + w.config.xaxis.title.offsetY + 20,
text: w.config.xaxis.title.text,
textAnchor: "middle",
fontSize: w.config.xaxis.title.style.fontSize,
fontFamily: w.config.xaxis.title.style.fontFamily,
fontWeight: w.config.xaxis.title.style.fontWeight,
foreColor: w.config.xaxis.title.style.color,
cssClass: `apexcharts-xaxis-title-text ${w.config.xaxis.title.style.cssClass}`
});
elYaxisTitle.add(elYAxisTitleText);
parent.add(elYaxisTitle);
}
}
/**
* @param {number} realIndex
* @param {boolean} yAxisOpposite
*/
yAxisTitleRotate(realIndex, yAxisOpposite) {
const w = this.w;
const graphics = new Graphics(this.w);
const elYAxisLabelsWrap = w.dom.baseEl.querySelector(
`.apexcharts-yaxis[rel='${realIndex}'] .apexcharts-yaxis-texts-g`
);
const yAxisLabelsCoord = elYAxisLabelsWrap ? elYAxisLabelsWrap.getBoundingClientRect() : { width: 0, height: 0 };
const yAxisTitle = w.dom.baseEl.querySelector(
`.apexcharts-yaxis[rel='${realIndex}'] .apexcharts-yaxis-title text`
);
const yAxisTitleCoord = yAxisTitle ? yAxisTitle.getBoundingClientRect() : { width: 0, height: 0 };
if (yAxisTitle) {
const x = this.xPaddingForYAxisTitle(
realIndex,
yAxisLabelsCoord,
yAxisTitleCoord,
yAxisOpposite
);
yAxisTitle.setAttribute("x", String(x.xPos - (yAxisOpposite ? 10 : 0)));
const titleRotatingCenter = graphics.rotateAroundCenter(yAxisTitle);
yAxisTitle.setAttribute(
"transform",
`rotate(${yAxisOpposite ? w.config.yaxis[realIndex].title.rotate * -1 : w.config.yaxis[realIndex].title.rotate} ${titleRotatingCenter.x} ${titleRotatingCenter.y})`
);
}
}
/**
* @param {number} realIndex
* @param {{width: number, height: number}} yAxisLabelsCoord
* @param {{width: number, height: number}} yAxisTitleCoord
* @param {boolean} yAxisOpposite
*/
xPaddingForYAxisTitle(realIndex, yAxisLabelsCoord, yAxisTitleCoord, yAxisOpposite) {
const w = this.w;
let x = 0;
let padd = 10;
if (w.config.yaxis[realIndex].title.text === void 0 || realIndex < 0) {
return { xPos: x, padd: 0 };
}
if (yAxisOpposite) {
x = yAxisLabelsCoord.width + w.config.yaxis[realIndex].title.offsetX + yAxisTitleCoord.width / 2 + padd / 2;
} else {
x = yAxisLabelsCoord.width * -1 + w.config.yaxis[realIndex].title.offsetX + padd / 2 + yAxisTitleCoord.width / 2;
if (w.globals.isBarHorizontal) {
padd = 25;
x = yAxisLabelsCoord.width * -1 - w.config.yaxis[realIndex].title.offsetX - padd;
}
}
return { xPos: x, padd };
}
/**
* @param {Array<{width: number, height: number}>} yaxisLabelCoords
* @param {Array<{width: number, height: number}>} yTitleCoords
*/
setYAxisXPosition(yaxisLabelCoords, yTitleCoords) {
const w = this.w;
let xLeft = 0;
let xRight = 0;
let leftOffsetX = 18;
let rightOffsetX = 1;
if (w.config.yaxis.length > 1) this.multipleYs = true;
w.config.yaxis.forEach((yaxe, index) => {
const shouldNotDrawAxis = w.globals.ignoreYAxisIndexes.includes(index) || !yaxe.show || yaxe.floating || yaxisLabelCoords[index].width === 0;
const axisWidth = yaxisLabelCoords[index].width + yTitleCoords[index].width;
if (!yaxe.opposite) {
xLeft = w.layout.translateX - leftOffsetX;
if (!shouldNotDrawAxis) leftOffsetX += axisWidth + 20;
w.globals.translateYAxisX[index] = xLeft + yaxe.labels.offsetX;
} else {
if (w.globals.isBarHorizontal) {
xRight = w.layout.gridWidth + w.layout.translateX - 1;
w.globals.translateYAxisX[index] = xRight - yaxe.labels.offsetX;
} else {
xRight = w.layout.gridWidth + w.layout.translateX + rightOffsetX;
if (!shouldNotDrawAxis) rightOffsetX += axisWidth + 20;
w.globals.translateYAxisX[index] = xRight - yaxe.labels.offsetX + 20;
}
}
});
}
setYAxisTextAlignments() {
const w = this.w;
const yaxis = Array.from(
w.dom.baseEl.getElementsByClassName("apexcharts-yaxis")
);
yaxis.forEach((y, index) => {
const yaxe = w.config.yaxis[index];
if (yaxe && !yaxe.floating && yaxe.labels.align !== void 0) {
const yAxisInner = w.dom.baseEl.querySelector(
`.apexcharts-yaxis[rel='${index}'] .apexcharts-yaxis-texts-g`
);
const yAxisTexts = Array.from(
w.dom.baseEl.querySelectorAll(
`.apexcharts-yaxis[rel='${index}'] .apexcharts-yaxis-label`
)
);
const rect = (
/** @type {Element} */
yAxisInner.getBoundingClientRect()
);
yAxisTexts.forEach((label) => {
label.setAttribute("text-anchor", yaxe.labels.align);
});
if (yaxe.labels.align === "left" && !yaxe.opposite) {
yAxisInner.setAttribute(
"transform",
`translate(-${rect.width}, 0)`
);
} else if (yaxe.labels.align === "center") {
yAxisInner.setAttribute(
"transform",
`translate(${rect.width / 2 * (!yaxe.opposite ? -1 : 1)}, 0)`
);
} else if (yaxe.labels.align === "right" && yaxe.opposite) {
yAxisInner.setAttribute(
"transform",
`translate(${rect.width}, 0)`
);
}
}
});
}
}
class Events {
/**
* @param {import('../types/internal').ChartStateW} w
* @param {import('../types/internal').ChartContext} ctx
*/
constructor(w, ctx) {
this.w = w;
this.ctx = ctx;
this.documentEvent = this.documentEvent.bind(this);
}
/**
* @param {string} name
* @param {Function} handler
*/
addEventListener(name2, handler) {
const w = this.w;
if (Object.prototype.hasOwnProperty.call(w.globals.events, name2)) {
w.globals.events[name2].push(handler);
} else {
w.globals.events[name2] = [handler];
}
}
/**
* @param {string} name
* @param {Function} handler
*/
removeEventListener(name2, handler) {
const w = this.w;
if (!Object.prototype.hasOwnProperty.call(w.globals.events, name2)) {
return;
}
const index = (
/** @type {Record<string,any>} */
w.globals.events[name2].indexOf(handler)
);
if (index !== -1) {
w.globals.events[name2].splice(
index,
1
);
}
}
/**
* @param {string} name
* @param {any[]} args
*/
fireEvent(name2, args) {
const w = this.w;
if (!Object.prototype.hasOwnProperty.call(w.globals.events, name2)) {
return;
}
if (!args || !args.length) {
args = [];
}
const evs = (
/** @type {Record<string,any>} */
w.globals.events[name2]
);
const l = evs.length;
for (let i = 0; i < l; i++) {
evs[i].apply(null, args);
}
}
setupEventHandlers() {
const w = this.w;
const me = this.ctx;
const clickableArea = w.dom.baseEl.querySelector(w.globals.chartClass);
this.ctx.eventList.forEach((event) => {
clickableArea == null ? void 0 : clickableArea.addEventListener(
event,
(e) => {
const capturedSeriesIndex = e.target.getAttribute("i") === null && w.interact.capturedSeriesIndex !== -1 ? w.interact.capturedSeriesIndex : e.target.getAttribute("i");
const capturedDataPointIndex = e.target.getAttribute("j") === null && w.interact.capturedDataPointIndex !== -1 ? w.interact.capturedDataPointIndex : e.target.getAttribute("j");
const opts = Object.assign({}, w, {
seriesIndex: w.globals.axisCharts ? capturedSeriesIndex : 0,
dataPointIndex: capturedDataPointIndex
});
if (e.type === "keydown") {
if (w.config.chart.accessibility.enabled && w.config.chart.accessibility.keyboard.enabled) {
if (me.ctx.keyboardNavigation) {
me.ctx.keyboardNavigation.handleKey(e);
}
if (typeof w.config.chart.events.keyDown === "function") {
w.config.chart.events.keyDown(e, me, opts);
}
me.ctx.events.fireEvent("keydown", [e, me, opts]);
}
} else if (e.type === "keyup") {
if (w.config.chart.accessibility.enabled && w.config.chart.accessibility.keyboard.enabled) {
if (typeof w.config.chart.events.keyUp === "function") {
w.config.chart.events.keyUp(e, me, opts);
}
me.ctx.events.fireEvent("keyup", [e, me, opts]);
}
} else if (e.type === "mousemove" || e.type === "touchmove") {
if (typeof w.config.chart.events.mouseMove === "function") {
w.config.chart.events.mouseMove(e, me, opts);
}
} else if (e.type === "mouseleave" || e.type === "touchleave") {
if (typeof w.config.chart.events.mouseLeave === "function") {
w.config.chart.events.mouseLeave(e, me, opts);
}
} else if (e.type === "mouseup" && e.which === 1 || e.type === "touchend") {
if (typeof w.config.chart.events.click === "function") {
w.config.chart.events.click(e, me, opts);
}
me.ctx.events.fireEvent("click", [e, me, opts]);
}
},
{ capture: false, passive: true }
);
});
this.ctx.eventList.forEach((event) => {
w.dom.baseEl.addEventListener(event, this.documentEvent, {
passive: true
});
});
this.ctx.core.setupBrushHandler();
}
/**
* @param {any} e
*/
documentEvent(e) {
const w = this.w;
const target = e.target.className;
if (e.type === "click") {
const elMenu = w.dom.baseEl.querySelector(".apexcharts-menu");
if (elMenu && elMenu.classList.contains("apexcharts-menu-open") && target !== "apexcharts-menu-icon") {
elMenu.classList.remove("apexcharts-menu-open");
}
}
w.interact.clientX = e.type === "touchmove" ? e.touches[0].clientX : e.clientX;
w.interact.clientY = e.type === "touchmove" ? e.touches[0].clientY : e.clientY;
}
}
class Localization {
/**
* @param {import('../../types/internal').ChartStateW} w
*/
constructor(w) {
this.w = w;
}
/**
* @param {string} localeName
*/
setCurrentLocaleValues(localeName) {
let locales = this.w.config.chart.locales;
const globalApex = Environment.getApex();
if (globalApex.chart && globalApex.chart.locales && globalApex.chart.locales.length > 0) {
locales = this.w.config.chart.locales.concat(globalApex.chart.locales);
}
const selectedLocale = locales.filter(
(c) => c.name === localeName
)[0];
if (selectedLocale) {
const ret = Utils$1.extend(en, selectedLocale);
this.w.globals.locale = ret.options;
} else {
throw new Error(
"Wrong locale name provided. Please make sure you set the correct locale name in options"
);
}
}
}
class Axes {
/**
* @param {import('../../types/internal').ChartStateW} w
* @param {import('../../types/internal').ChartContext} ctx
*/
constructor(w, ctx) {
this.w = w;
this.ctx = ctx;
}
/**
* @param {string} type
* @param {any} elgrid
*/
drawAxis(type, elgrid) {
const gl = this.w.globals;
const cnf = this.w.config;
const xAxis = new XAxis(this.w, this.ctx, elgrid);
const yAxis = new YAxis(this.w, { theme: this.ctx.theme, timeScale: this.ctx.timeScale }, elgrid);
if (gl.axisCharts && type !== "radar") {
let elXaxis, elYaxis;
if (gl.isBarHorizontal) {
elYaxis = yAxis.drawYaxisInversed(0);
elXaxis = xAxis.drawXaxisInversed(0);
this.w.dom.elGraphical.add(elXaxis);
this.w.dom.elGraphical.add(elYaxis);
} else {
elXaxis = xAxis.drawXaxis();
this.w.dom.elGraphical.add(elXaxis);
cnf.yaxis.map((yaxe, index) => {
if (gl.ignoreYAxisIndexes.indexOf(index) === -1) {
elYaxis = yAxis.drawYaxis(index);
this.w.dom.Paper.add(elYaxis);
if (this.w.config.grid.position === "back") {
const inner = this.w.dom.Paper.children()[1];
if (inner) {
inner.remove();
this.w.dom.Paper.add(inner);
}
}
}
});
}
}
}
}
class Crosshairs {
/**
* @param {import('../types/internal').ChartStateW} w
*/
constructor(w) {
this.w = w;
}
drawXCrosshairs() {
const w = this.w;
const graphics = new Graphics(this.w);
const filters = new Filters(this.w);
const crosshairGradient = w.config.xaxis.crosshairs.fill.gradient;
const crosshairShadow = w.config.xaxis.crosshairs.dropShadow;
const fillType = w.config.xaxis.crosshairs.fill.type;
const gradientFrom = crosshairGradient.colorFrom;
const gradientTo = crosshairGradient.colorTo;
const opacityFrom = crosshairGradient.opacityFrom;
const opacityTo = crosshairGradient.opacityTo;
const stops = crosshairGradient.stops;
const shadow = "none";
const dropShadow = crosshairShadow.enabled;
const shadowLeft = crosshairShadow.left;
const shadowTop = crosshairShadow.top;
const shadowBlur = crosshairShadow.blur;
const shadowColor = crosshairShadow.color;
const shadowOpacity = crosshairShadow.opacity;
let xcrosshairsFill = w.config.xaxis.crosshairs.fill.color;
if (w.config.xaxis.crosshairs.show) {
if (fillType === "gradient") {
xcrosshairsFill = graphics.drawGradient(
"vertical",
gradientFrom,
gradientTo,
opacityFrom,
opacityTo,
null,
stops,
[]
);
}
let xcrosshairs = graphics.drawRect();
if (w.config.xaxis.crosshairs.width === 1) {
xcrosshairs = graphics.drawLine(0, 0, 0, 0);
}
let gridHeight = w.layout.gridHeight;
if (!Utils$1.isNumber(gridHeight) || gridHeight < 0) {
gridHeight = 0;
}
let crosshairsWidth = w.config.xaxis.crosshairs.width;
if (!Utils$1.isNumber(crosshairsWidth) || Number(crosshairsWidth) < 0) {
crosshairsWidth = 0;
}
xcrosshairs.attr({
class: "apexcharts-xcrosshairs",
x: 0,
y: 0,
y2: gridHeight,
width: crosshairsWidth,
height: gridHeight,
fill: xcrosshairsFill,
filter: shadow,
"fill-opacity": w.config.xaxis.crosshairs.opacity,
stroke: w.config.xaxis.crosshairs.stroke.color,
"stroke-width": w.config.xaxis.crosshairs.stroke.width,
"stroke-dasharray": w.config.xaxis.crosshairs.stroke.dashArray
});
if (dropShadow) {
xcrosshairs = filters.dropShadow(xcrosshairs, {
left: shadowLeft,
top: shadowTop,
blur: shadowBlur,
color: shadowColor,
opacity: shadowOpacity
});
}
w.dom.elGraphical.add(xcrosshairs);
}
}
drawYCrosshairs() {
const w = this.w;
const graphics = new Graphics(this.w);
const crosshair = (
/** @type {any[]} */
w.config.yaxis[0].crosshairs
);
const offX = w.globals.barPadForNumericAxis;
if (
/** @type {any[]} */
w.config.yaxis[0].crosshairs.show
) {
const ycrosshairs = graphics.drawLine(
-offX,
0,
w.layout.gridWidth + offX,
0,
crosshair.stroke.color,
crosshair.stroke.dashArray,
crosshair.stroke.width
);
ycrosshairs.attr({
class: "apexcharts-ycrosshairs"
});
w.dom.elGraphical.add(ycrosshairs);
}
const ycrosshairsHidden = graphics.drawLine(
-offX,
0,
w.layout.gridWidth + offX,
0,
crosshair.stroke.color,
0,
0
);
ycrosshairsHidden.attr({
class: "apexcharts-ycrosshairs-hidden"
});
w.dom.elGraphical.add(ycrosshairsHidden);
}
}
class Responsive {
/**
* @param {import('../types/internal').ChartStateW} w
*/
constructor(w) {
this.w = w;
this._activeBreakpoint = null;
}
// the opts parameter if not null has to be set overriding everything
// as the opts is set by user externally
/**
* @param {object} opts
*/
checkResponsiveConfig(opts) {
const w = this.w;
const cnf = w.config;
if (cnf.responsive.length === 0) return;
const res = cnf.responsive.slice();
res.sort(
(a, b) => a.breakpoint > b.breakpoint ? 1 : b.breakpoint > a.breakpoint ? -1 : 0
).reverse();
const config = new Config({});
const iterateResponsiveOptions = (newOptions = {}) => {
const largestBreakpoint = res[0].breakpoint;
const width = Environment.isBrowser() ? window.innerWidth > 0 ? window.innerWidth : screen.width : 0;
if (width > largestBreakpoint) {
if (this._activeBreakpoint !== null) {
if (!w.globals.initialConfig) return;
const initialConfig = Utils$1.clone(w.globals.initialConfig);
initialConfig.series = Utils$1.clone(w.config.series);
const options2 = CoreUtils.extendArrayProps(config, initialConfig, w);
newOptions = Utils$1.extend(options2, newOptions);
this.overrideResponsiveOptions(newOptions);
this._activeBreakpoint = null;
}
} else {
for (let i = 0; i < res.length; i++) {
if (width < res[i].breakpoint) {
newOptions = CoreUtils.extendArrayProps(config, res[i].options, w);
newOptions = Utils$1.extend(w.config, newOptions);
this.overrideResponsiveOptions(newOptions);
this._activeBreakpoint = res[i].breakpoint;
}
}
}
};
if (opts) {
let options2 = CoreUtils.extendArrayProps(config, opts, w);
options2 = Utils$1.extend(w.config, options2);
options2 = Utils$1.extend(options2, opts);
iterateResponsiveOptions(options2);
} else {
iterateResponsiveOptions({});
}
}
/**
* @param {Record<string, any>} newOptions
*/
overrideResponsiveOptions(newOptions) {
const newConfig = new Config(newOptions).init({ responsiveOverride: true });
this.w.config = /** @type {any} */
newConfig;
}
}
class Series {
/**
* @param {import('../types/internal').ChartStateW} w
* @param {{ toggleDataSeries?: Function, revertDefaultAxisMinMax?: Function, updateSeries?: Function }} [callbacks]
*/
constructor(w, {
toggleDataSeries = void 0,
revertDefaultAxisMinMax = void 0,
updateSeries = void 0
} = {}) {
this.w = w;
this._toggleDataSeries = toggleDataSeries || null;
this._revertDefaultAxisMinMax = revertDefaultAxisMinMax || null;
this._updateSeries = updateSeries || null;
this.legendInactiveClass = "legend-mouseover-inactive";
}
clearSeriesCache() {
const w = this.w;
if (w.globals.cachedSelectors) {
delete w.globals.cachedSelectors.allSeriesEls;
delete w.globals.cachedSelectors.highlightSeriesEls;
}
}
getAllSeriesEls() {
const w = this.w;
const cacheKey = "allSeriesEls";
if (!w.globals.cachedSelectors[cacheKey]) {
w.globals.cachedSelectors[cacheKey] = /** @type {any} */
w.dom.baseEl.getElementsByClassName(`apexcharts-series`);
}
return w.globals.cachedSelectors[cacheKey];
}
/**
* @param {string} seriesName
*/
getSeriesByName(seriesName) {
return this.w.dom.baseEl.querySelector(
`.apexcharts-inner .apexcharts-series[seriesName='${Utils$1.escapeString(
seriesName
)}']`
);
}
/**
* @param {string} seriesName
*/
isSeriesHidden(seriesName) {
var _a;
const targetElement = this.getSeriesByName(seriesName);
const el = (
/** @type {Element} */
targetElement
);
const realIndex = parseInt((_a = el.getAttribute("data:realIndex")) != null ? _a : "0", 10);
const isHidden = el.classList.contains("apexcharts-series-collapsed");
return { isHidden, realIndex };
}
/**
* @param {any} elSeries
* @param {number} index
*/
addCollapsedClassToSeries(elSeries, index) {
Series.addCollapsedClassToSeries(this.w, elSeries, index);
}
/**
* @param {import('../types/internal').ChartStateW} w
* @param {any} elSeries
* @param {number} index
*/
static addCollapsedClassToSeries(w, elSeries, index) {
function iterateOnAllCollapsedSeries(series) {
for (let cs = 0; cs < series.length; cs++) {
if (series[cs].index === index) {
elSeries.node.classList.add("apexcharts-series-collapsed");
}
}
}
iterateOnAllCollapsedSeries(w.globals.collapsedSeries);
iterateOnAllCollapsedSeries(w.globals.ancillaryCollapsedSeries);
}
/**
* @param {string} seriesName
*/
toggleSeries(seriesName) {
var _a;
const isSeriesHidden = this.isSeriesHidden(seriesName);
(_a = this._toggleDataSeries) == null ? void 0 : _a.call(this, isSeriesHidden.realIndex, isSeriesHidden.isHidden);
return isSeriesHidden.isHidden;
}
/**
* @param {string} seriesName
*/
showSeries(seriesName) {
var _a;
const isSeriesHidden = this.isSeriesHidden(seriesName);
if (isSeriesHidden.isHidden) {
(_a = this._toggleDataSeries) == null ? void 0 : _a.call(this, isSeriesHidden.realIndex, true);
}
}
/**
* @param {string} seriesName
*/
hideSeries(seriesName) {
var _a;
const isSeriesHidden = this.isSeriesHidden(seriesName);
if (!isSeriesHidden.isHidden) {
(_a = this._toggleDataSeries) == null ? void 0 : _a.call(this, isSeriesHidden.realIndex, false);
}
}
resetSeries(shouldUpdateChart = true, shouldResetZoom = true, shouldResetCollapsed = true) {
var _a, _b;
const w = this.w;
this.clearSeriesCache();
let series = Utils$1.clone(w.globals.initialSeries);
w.globals.previousPaths = [];
if (shouldResetCollapsed) {
w.globals.collapsedSeries = [];
w.globals.ancillaryCollapsedSeries = [];
w.globals.collapsedSeriesIndices = [];
w.globals.ancillaryCollapsedSeriesIndices = [];
} else {
series = this.emptyCollapsedSeries(series);
}
w.config.series = series;
if (shouldUpdateChart) {
if (shouldResetZoom) {
w.interact.zoomed = false;
(_a = this._revertDefaultAxisMinMax) == null ? void 0 : _a.call(this);
}
(_b = this._updateSeries) == null ? void 0 : _b.call(
this,
series,
w.config.chart.animations.dynamicAnimation.enabled
);
}
}
/**
* @param {any[]} series
*/
emptyCollapsedSeries(series) {
const w = this.w;
for (let i = 0; i < series.length; i++) {
if (w.globals.collapsedSeriesIndices.indexOf(i) > -1) {
series[i].data = [];
}
}
return series;
}
/**
* @param {string} seriesName
*/
highlightSeries(seriesName) {
var _a;
const w = this.w;
const targetElement = this.getSeriesByName(seriesName);
const realIndex = parseInt(
(_a = targetElement == null ? void 0 : targetElement.getAttribute("data:realIndex")) != null ? _a : "",
10
);
const cacheKey = "highlightSeriesEls";
let allSeriesEls = w.globals.cachedSelectors[cacheKey];
if (!allSeriesEls) {
allSeriesEls = w.dom.baseEl.querySelectorAll(
`.apexcharts-series, .apexcharts-datalabels, .apexcharts-yaxis`
);
w.globals.cachedSelectors[cacheKey] = allSeriesEls;
}
let seriesEl = null;
let dataLabelEl = null;
let yaxisEl = null;
if (w.globals.axisCharts || w.config.chart.type === "radialBar") {
if (w.globals.axisCharts) {
seriesEl = w.dom.baseEl.querySelector(
`.apexcharts-series[data\\:realIndex='${realIndex}']`
);
dataLabelEl = w.dom.baseEl.querySelector(
`.apexcharts-datalabels[data\\:realIndex='${realIndex}']`
);
const yaxisIndex = w.globals.seriesYAxisReverseMap[realIndex];
yaxisEl = w.dom.baseEl.querySelector(
`.apexcharts-yaxis[rel='${yaxisIndex}']`
);
} else {
seriesEl = w.dom.baseEl.querySelector(
`.apexcharts-series[rel='${realIndex + 1}']`
);
}
} else {
seriesEl = w.dom.baseEl.querySelector(
`.apexcharts-series[rel='${realIndex + 1}'] path`
);
}
for (let se = 0; se < allSeriesEls.length; se++) {
const serEl = (
/** @type {Element} */
allSeriesEls[se]
);
serEl.classList.add(this.legendInactiveClass);
}
if (seriesEl) {
if (!w.globals.axisCharts) {
const parentEl = (
/** @type {Element} */
seriesEl.parentNode
);
parentEl == null ? void 0 : parentEl.classList.remove(this.legendInactiveClass);
}
seriesEl.classList.remove(this.legendInactiveClass);
if (dataLabelEl !== null) {
dataLabelEl.classList.remove(this.legendInactiveClass);
}
if (yaxisEl !== null) {
yaxisEl.classList.remove(this.legendInactiveClass);
}
} else {
for (let se = 0; se < allSeriesEls.length; se++) {
const serEl = (
/** @type {Element} */
allSeriesEls[se]
);
serEl.classList.remove(this.legendInactiveClass);
}
}
}
/**
* @param {Event} e
* @param {any} targetElement
*/
toggleSeriesOnHover(e, targetElement) {
const w = this.w;
if (!targetElement) targetElement = e.target;
const allSeriesEls = w.dom.baseEl.querySelectorAll(
`.apexcharts-series, .apexcharts-datalabels, .apexcharts-yaxis`
);
if (e.type === "mousemove") {
const realIndex = parseInt(targetElement.getAttribute("rel"), 10) - 1;
this.highlightSeries(w.seriesData.seriesNames[realIndex]);
} else if (e.type === "mouseout") {
for (let se = 0; se < allSeriesEls.length; se++) {
allSeriesEls[se].classList.remove(this.legendInactiveClass);
}
}
}
/**
* @param {Event} e
* @param {any} targetElement
*/
highlightRangeInSeries(e, targetElement) {
const w = this.w;
const allHeatMapElements = w.dom.baseEl.getElementsByClassName(
"apexcharts-heatmap-rect"
);
const activeInactive = (action) => {
for (let i = 0; i < allHeatMapElements.length; i++) {
const actionFn = (
/** @type {any} */
allHeatMapElements[i].classList[action]
);
if (typeof actionFn === "function") {
actionFn.call(
/** @type {any} */
allHeatMapElements[i].classList,
this.legendInactiveClass
);
}
}
};
const removeInactiveClassFromHoveredRange = (range, rangeMax) => {
for (let i = 0; i < allHeatMapElements.length; i++) {
const val = Number(allHeatMapElements[i].getAttribute("val"));
if (val >= range.from && (val < range.to || range.to === rangeMax && val === rangeMax)) {
allHeatMapElements[i].classList.remove(this.legendInactiveClass);
}
}
};
if (e.type === "mousemove") {
const seriesCnt = parseInt(targetElement.getAttribute("rel"), 10) - 1;
activeInactive("add");
const ranges = w.config.plotOptions.heatmap.colorScale.ranges;
const range = ranges[seriesCnt];
const rangeMax = ranges.reduce(
(acc, cur) => Math.max(acc, cur.to),
0
);
removeInactiveClassFromHoveredRange(range, rangeMax);
} else if (e.type === "mouseout") {
activeInactive("remove");
}
}
/**
* @param {string[]} chartTypes
*/
getActiveConfigSeriesIndex(order = "asc", chartTypes = []) {
const w = this.w;
let activeIndex = 0;
if (w.config.series.length > 1) {
const activeSeriesIndex = w.config.series.map((s, index) => {
const checkChartType = () => {
if (w.globals.comboCharts) {
return chartTypes.length === 0 || chartTypes.length && chartTypes.indexOf(
/** @type {Record<string,any>} */
w.config.series[index].type
) > -1;
}
return true;
};
const hasData = (
/** @type {any} */
s.data && /** @type {any} */
s.data.length > 0 && w.globals.collapsedSeriesIndices.indexOf(index) === -1
);
return hasData && checkChartType() ? index : -1;
});
for (let a = order === "asc" ? 0 : activeSeriesIndex.length - 1; order === "asc" ? a < activeSeriesIndex.length : a >= 0; order === "asc" ? a++ : a--) {
if (activeSeriesIndex[a] !== -1) {
activeIndex = activeSeriesIndex[a];
break;
}
}
}
return activeIndex;
}
getBarSeriesIndices() {
const w = this.w;
if (w.globals.comboCharts) {
return this.w.config.series.map((s, i) => {
return s.type === "bar" || s.type === "column" ? i : -1;
}).filter((i) => {
return i !== -1;
});
}
return this.w.config.series.map((s, i) => {
return i;
});
}
getPreviousPaths() {
var _a, _b, _c, _d;
const w = this.w;
w.globals.previousPaths = [];
function pushPaths(seriesEls, i, type) {
const paths = seriesEls[i].childNodes;
const dArr = {
type,
paths: (
/** @type {any[]} */
[]
),
realIndex: seriesEls[i].getAttribute("data:realIndex")
};
for (let j = 0; j < paths.length; j++) {
if (paths[j].hasAttribute("pathTo")) {
const d = paths[j].getAttribute("pathTo");
dArr.paths.push({ d });
}
}
w.globals.previousPaths.push(dArr);
}
const getPaths = (chartType) => {
return w.dom.baseEl.querySelectorAll(
`.apexcharts-${chartType}-series .apexcharts-series`
);
};
const chartTypes = [
"line",
"area",
"bar",
"rangebar",
"rangeArea",
"candlestick",
"radar"
];
chartTypes.forEach((type) => {
const paths = getPaths(type);
for (let p = 0; p < paths.length; p++) {
pushPaths(paths, p, type);
}
});
const heatTreeSeries = w.dom.baseEl.querySelectorAll(
`.apexcharts-${w.config.chart.type} .apexcharts-series`
);
if (heatTreeSeries.length > 0) {
for (let h = 0; h < heatTreeSeries.length; h++) {
const seriesEls = w.dom.baseEl.querySelectorAll(
`.apexcharts-${w.config.chart.type} .apexcharts-series[data\\:realIndex='${h}'] rect`
);
const dArr = [];
for (let i = 0; i < seriesEls.length; i++) {
const getAttr = (x) => {
return (
/** @type {Element} */
seriesEls[i].getAttribute(x)
);
};
const rect = {
x: parseFloat((_a = getAttr("x")) != null ? _a : "0"),
y: parseFloat((_b = getAttr("y")) != null ? _b : "0"),
width: parseFloat((_c = getAttr("width")) != null ? _c : "0"),
height: parseFloat((_d = getAttr("height")) != null ? _d : "0")
};
dArr.push({
rect,
color: seriesEls[i].getAttribute("color")
});
}
w.globals.previousPaths.push(dArr);
}
}
if (!w.globals.axisCharts) {
w.globals.previousPaths = w.seriesData.series;
}
}
clearPreviousPaths() {
const w = this.w;
w.globals.previousPaths = [];
w.globals.allSeriesCollapsed = false;
}
handleNoData() {
const w = this.w;
const me = this;
const noDataOpts = w.config.noData;
const graphics = new Graphics(me.w);
let x = w.globals.svgWidth / 2;
let y = w.globals.svgHeight / 2;
let textAnchor = "middle";
w.globals.noData = true;
w.globals.animationEnded = true;
if (noDataOpts.align === "left") {
x = 10;
textAnchor = "start";
} else if (noDataOpts.align === "right") {
x = w.globals.svgWidth - 10;
textAnchor = "end";
}
if (noDataOpts.verticalAlign === "top") {
y = 50;
} else if (noDataOpts.verticalAlign === "bottom") {
y = w.globals.svgHeight - 50;
}
x = x + noDataOpts.offsetX;
y = y + parseInt(noDataOpts.style.fontSize, 10) + 2 + noDataOpts.offsetY;
if (noDataOpts.text !== void 0 && noDataOpts.text !== "") {
const titleText = graphics.drawText({
x,
y,
text: noDataOpts.text,
textAnchor,
fontSize: noDataOpts.style.fontSize,
fontFamily: noDataOpts.style.fontFamily,
foreColor: noDataOpts.style.color,
opacity: 1,
cssClass: "apexcharts-text-nodata"
});
w.dom.Paper.add(titleText);
}
}
// When user clicks on legends, the collapsed series is filled with [0,0,0,...,0]
// This is because we don't want to alter the series' length as it is used at many places
/**
* @param {any[]} series
*/
setNullSeriesToZeroValues(series) {
const w = this.w;
for (let sl = 0; sl < series.length; sl++) {
if (series[sl].length === 0) {
for (let j = 0; j < series[w.globals.maxValsInArrayIndex].length; j++) {
series[sl].push(0);
}
}
}
return series;
}
hasAllSeriesEqualX() {
let equalLen = true;
const w = this.w;
const filteredSerX = this.filteredSeriesX();
for (let i = 0; i < filteredSerX.length - 1; i++) {
if (filteredSerX[i][0] !== filteredSerX[i + 1][0]) {
equalLen = false;
break;
}
}
w.globals.allSeriesHasEqualX = equalLen;
return equalLen;
}
filteredSeriesX() {
const w = this.w;
const filteredSeriesX = w.seriesData.seriesX.map(
(ser) => ser.length > 0 ? ser : []
);
return filteredSeriesX;
}
}
class Theme {
/**
* @param {import('../types/internal').ChartStateW} w
*/
constructor(w) {
this.w = w;
this.colors = [];
this.isColorFn = false;
this.isHeatmapDistributed = this.checkHeatmapDistributed();
this.isBarDistributed = this.checkBarDistributed();
}
checkHeatmapDistributed() {
const { chart, plotOptions } = this.w.config;
return chart.type === "treemap" && plotOptions.treemap && plotOptions.treemap.distributed || chart.type === "heatmap" && plotOptions.heatmap && plotOptions.heatmap.distributed;
}
checkBarDistributed() {
const { chart, plotOptions } = this.w.config;
return plotOptions.bar && plotOptions.bar.distributed && (chart.type === "bar" || chart.type === "rangeBar");
}
init() {
this.setDefaultColors();
}
setDefaultColors() {
var _a;
const w = this.w;
const utils = new Utils$1();
w.dom.elWrap.classList.add(
`apexcharts-theme-${w.config.theme.mode || "light"}`
);
const colorBlindMode = (_a = w.config.theme.accessibility) == null ? void 0 : _a.colorBlindMode;
if (colorBlindMode) {
w.globals.colors = this.getColorBlindColors(colorBlindMode);
this.applySeriesColors(w.seriesData.seriesColors, w.globals.colors);
const defaultColors2 = w.globals.colors.slice();
this.pushExtraColors(w.globals.colors);
this.applyColorTypes(["fill", "stroke"], defaultColors2);
this.applyDataLabelsColors(defaultColors2);
this.applyRadarPolygonsColors();
this.applyMarkersColors(defaultColors2);
if (colorBlindMode === "highContrast") {
w.dom.elWrap.classList.add("apexcharts-high-contrast");
}
return;
}
const configColors = [...w.config.colors || w.config.fill.colors || []];
w.globals.colors = this.getColors(configColors);
this.applySeriesColors(w.seriesData.seriesColors, w.globals.colors);
if (w.config.theme.monochrome.enabled) {
w.globals.colors = this.getMonochromeColors(
w.config.theme.monochrome,
w.seriesData.series,
utils
);
}
const defaultColors = w.globals.colors.slice();
this.pushExtraColors(w.globals.colors);
this.applyColorTypes(["fill", "stroke"], defaultColors);
this.applyDataLabelsColors(defaultColors);
this.applyRadarPolygonsColors();
this.applyMarkersColors(defaultColors);
}
/**
* @param {any[]} configColors
*/
getColors(configColors) {
const w = this.w;
if (!configColors || configColors.length === 0) {
return this.predefined();
}
if (Array.isArray(configColors) && configColors.length > 0 && typeof configColors[0] === "function") {
this.isColorFn = true;
return w.config.series.map((s, i) => {
const c = configColors[i] || configColors[0];
return typeof c === "function" ? c({
value: w.globals.axisCharts ? w.seriesData.series[i][0] || 0 : w.seriesData.series[i],
seriesIndex: i,
dataPointIndex: i,
w: this.w
}) : c;
});
}
return configColors;
}
/**
* @param {any[]} seriesColors
* @param {any[]} globalsColors
*/
applySeriesColors(seriesColors, globalsColors) {
seriesColors.forEach((c, i) => {
if (c) {
globalsColors[i] = c;
}
});
}
/**
* @param {Record<string, any>} monochrome
* @param {any[]} series
* @param {any} utils
*/
getMonochromeColors(monochrome, series, utils) {
const { color, shadeIntensity, shadeTo } = monochrome;
const glsCnt = this.isBarDistributed || this.isHeatmapDistributed ? series[0].length * series.length : series.length;
const part = 1 / (glsCnt / shadeIntensity);
let percent = 0;
return Array.from({ length: glsCnt }, () => {
const newColor = shadeTo === "dark" ? utils.shadeColor(percent * -1, color) : utils.shadeColor(percent, color);
percent += part;
return newColor;
});
}
/**
* @param {string[]} colorTypes
* @param {string[]} defaultColors
*/
applyColorTypes(colorTypes, defaultColors) {
const w = this.w;
colorTypes.forEach((c) => {
w.globals[c].colors = w.config[c].colors === void 0 ? this.isColorFn ? w.config.colors : defaultColors : w.config[c].colors.slice();
this.pushExtraColors(
/** @type {Record<string,any>} */
w.globals[c].colors
);
});
}
/**
* @param {string[]} defaultColors
*/
applyDataLabelsColors(defaultColors) {
const w = this.w;
w.globals.dataLabels.style.colors = w.config.dataLabels.style.colors === void 0 ? defaultColors : w.config.dataLabels.style.colors.slice();
this.pushExtraColors(w.globals.dataLabels.style.colors, 50);
}
applyRadarPolygonsColors() {
const w = this.w;
w.globals.radarPolygons.fill.colors = w.config.plotOptions.radar.polygons.fill.colors === void 0 ? [w.config.theme.mode === "dark" ? "#343A3F" : "none"] : w.config.plotOptions.radar.polygons.fill.colors.slice();
this.pushExtraColors(w.globals.radarPolygons.fill.colors, 20);
}
/**
* @param {string[]} defaultColors
*/
applyMarkersColors(defaultColors) {
const w = this.w;
w.globals.markers.colors = w.config.markers.colors === void 0 ? defaultColors : w.config.markers.colors.slice();
this.pushExtraColors(w.globals.markers.colors);
}
/**
* @param {any} colorSeries
* @param {number} [length]
* @param {boolean | null} [distributed]
*/
pushExtraColors(colorSeries, length, distributed = null) {
const w = this.w;
let len = length || w.seriesData.series.length;
if (distributed === null) {
distributed = this.isBarDistributed || this.isHeatmapDistributed || w.config.chart.type === "heatmap" && w.config.plotOptions.heatmap && w.config.plotOptions.heatmap.colorScale.inverse;
}
if (distributed && w.seriesData.series.length) {
len = w.seriesData.series[w.globals.maxValsInArrayIndex].length * w.seriesData.series.length;
}
if (colorSeries.length < len) {
const diff = len - colorSeries.length;
for (let i = 0; i < diff; i++) {
colorSeries.push(colorSeries[i]);
}
}
}
/**
* @param {'light' | 'dark'} mode
*/
getColorBlindColors(mode) {
const palettes = getThemePalettes();
const map = {
deuteranopia: palettes.cvdDeuteranopia,
protanopia: palettes.cvdProtanopia,
tritanopia: palettes.cvdTritanopia,
highContrast: palettes.highContrast
};
return (
/** @type {Record<string,any>} */
/** @type {any} */
(map[mode] || palettes.palette1).slice()
);
}
/**
* @param {Record<string, any>} options
*/
updateThemeOptions(options2) {
options2.chart = options2.chart || {};
options2.tooltip = options2.tooltip || {};
const mode = options2.theme.mode;
const palette = mode === "dark" ? "palette4" : mode === "light" ? "palette1" : options2.theme.palette || "palette1";
const foreColor = mode === "dark" ? "#f6f7f8" : mode === "light" ? "#373d3f" : options2.chart.foreColor || "#373d3f";
options2.tooltip.theme = mode || "light";
options2.chart.foreColor = foreColor;
options2.theme.palette = palette;
return options2;
}
predefined() {
const palette = this.w.config.theme.palette;
const palettes = getThemePalettes();
return (
/** @type {Record<string,any>} */
palettes[palette] || palettes.palette1
);
}
}
class TitleSubtitle {
/**
* @param {import('../types/internal').ChartStateW} w
*/
constructor(w) {
this.w = w;
}
draw() {
this.drawTitleSubtitle("title");
this.drawTitleSubtitle("subtitle");
}
/**
* @param {'title' | 'subtitle'} type
*/
drawTitleSubtitle(type) {
const w = this.w;
const tsConfig = type === "title" ? w.config.title : w.config.subtitle;
let x = w.globals.svgWidth / 2;
let y = tsConfig.offsetY;
let textAnchor = "middle";
if (tsConfig.align === "left") {
x = 10;
textAnchor = "start";
} else if (tsConfig.align === "right") {
x = w.globals.svgWidth - 10;
textAnchor = "end";
}
x = x + tsConfig.offsetX;
y = y + parseInt(tsConfig.style.fontSize, 10) + tsConfig.margin / 2;
if (tsConfig.text !== void 0) {
const graphics = new Graphics(this.w);
const titleText = graphics.drawText({
x,
y,
text: tsConfig.text,
textAnchor,
fontSize: tsConfig.style.fontSize,
fontFamily: tsConfig.style.fontFamily,
fontWeight: tsConfig.style.fontWeight,
foreColor: tsConfig.style.color,
opacity: 1
});
titleText.node.setAttribute("class", `apexcharts-${type}-text`);
w.dom.Paper.add(titleText);
}
}
}
let Helpers$4 = class Helpers {
/**
* @param {import('./Dimensions').default} dCtx
*/
constructor(dCtx) {
this.w = dCtx.w;
this.dCtx = dCtx;
}
/**
* Get Chart Title/Subtitle Dimensions
* @memberof Dimensions
* @return {{width: number, height: number}}
* @param {string} type
**/
getTitleSubtitleCoords(type) {
const w = this.w;
let width = 0;
let height = 0;
const floating = type === "title" ? w.config.title.floating : w.config.subtitle.floating;
const el = w.dom.baseEl.querySelector(`.apexcharts-${type}-text`);
if (el !== null && !floating) {
const coord = el.getBoundingClientRect();
width = coord.width;
height = w.globals.axisCharts ? coord.height + 5 : coord.height;
}
return {
width,
height
};
}
getLegendsRect() {
const w = this.w;
const elLegendWrap = w.dom.elLegendWrap;
if (!w.config.legend.height && (w.config.legend.position === "top" || w.config.legend.position === "bottom")) {
if (elLegendWrap)
elLegendWrap.style.maxHeight = w.globals.svgHeight / 2 + "px";
}
const lgRect = (
/** @type {any} */
Object.assign({}, Utils$1.getBoundingClientRect(elLegendWrap))
);
if (elLegendWrap !== null && !w.config.legend.floating && w.config.legend.show) {
this.dCtx.lgRect = {
x: lgRect.x,
y: lgRect.y,
height: lgRect.height,
width: lgRect.height === 0 ? 0 : lgRect.width
};
} else {
this.dCtx.lgRect = {
x: 0,
y: 0,
height: 0,
width: 0
};
}
if (w.config.legend.position === "left" || w.config.legend.position === "right") {
if (this.dCtx.lgRect.width * 1.5 > w.globals.svgWidth) {
this.dCtx.lgRect.width = w.globals.svgWidth / 1.5;
}
}
return this.dCtx.lgRect;
}
/**
* Get Y Axis Dimensions
* @memberof Dimensions
* @return {{width: number, height: number}}
**/
getDatalabelsRect() {
const w = this.w;
const allLabels = [];
w.config.series.forEach(
(serie, seriesIndex) => {
serie.data.forEach(
(datum, dataPointIndex) => {
const getText = (v) => {
return w.config.dataLabels.formatter(v, {
seriesIndex,
dataPointIndex,
w
});
};
const labelText = getText(
w.seriesData.series[seriesIndex][dataPointIndex]
);
allLabels.push(labelText);
}
);
}
);
const val = Utils$1.getLargestStringFromArr(allLabels);
const graphics = new Graphics(this.w);
const dataLabelsStyle = w.config.dataLabels.style;
const labelrect = graphics.getTextRects(
val,
parseInt(dataLabelsStyle.fontSize).toString(),
dataLabelsStyle.fontFamily
);
return {
width: labelrect.width * 1.05,
height: labelrect.height
};
}
/**
* @param {any} val
* @param {any[]} arr
*/
getLargestStringFromMultiArr(val, arr) {
const w = this.w;
let valArr = val;
if (w.axisFlags.isMultiLineX) {
const maxArrs = arr.map((xl) => {
return Array.isArray(xl) ? xl.length : 1;
});
const maxArrLen = Math.max(...maxArrs);
const maxArrIndex = maxArrs.indexOf(maxArrLen);
valArr = arr[maxArrIndex];
}
return valArr;
}
};
class DimXAxis {
/**
* @param {import('./Dimensions').default} dCtx
*/
constructor(dCtx) {
this.w = dCtx.w;
this.dCtx = dCtx;
}
/**
* Get X Axis Dimensions
* @memberof Dimensions
* @return {{width: number, height: number}}
**/
getxAxisLabelsCoords() {
const w = this.w;
let xaxisLabels = w.labelData.labels.slice();
if (w.config.xaxis.convertedCatToNumeric && xaxisLabels.length === 0) {
xaxisLabels = w.labelData.categoryLabels;
}
let rect;
if (w.labelData.timescaleLabels.length > 0) {
const coords = this.getxAxisTimeScaleLabelsCoords();
rect = {
width: coords.width,
height: coords.height
};
w.layout.rotateXLabels = false;
} else {
this.dCtx.lgWidthForSideLegends = (w.config.legend.position === "left" || w.config.legend.position === "right") && !w.config.legend.floating ? this.dCtx.lgRect.width : 0;
const xlbFormatter = w.formatters.xLabelFormatter;
let val = Utils$1.getLargestStringFromArr(xaxisLabels);
let valArr = this.dCtx.dimHelpers.getLargestStringFromMultiArr(
val,
xaxisLabels
);
if (w.globals.isBarHorizontal) {
val = w.globals.yAxisScale[0].result.reduce(
(a, b) => a.length > b.length ? a : b,
0
);
valArr = val;
}
const xFormat = new Formatters(this.w);
const timestamp = val;
val = xFormat.xLabelFormat(
/** @type {Function} */
xlbFormatter,
val,
timestamp,
{
i: void 0,
dateFormatter: new DateTime(this.w).formatDate,
w
}
);
valArr = xFormat.xLabelFormat(
/** @type {Function} */
xlbFormatter,
valArr,
timestamp,
{
i: void 0,
dateFormatter: new DateTime(this.w).formatDate,
w
}
);
if (w.config.xaxis.convertedCatToNumeric && typeof val === "undefined" || String(val).trim() === "") {
val = "1";
valArr = val;
}
const graphics = new Graphics(this.w);
let xLabelrect = graphics.getTextRects(
val,
w.config.xaxis.labels.style.fontSize
);
let xArrLabelrect = xLabelrect;
if (val !== valArr) {
xArrLabelrect = graphics.getTextRects(
valArr,
w.config.xaxis.labels.style.fontSize
);
}
rect = {
width: xLabelrect.width >= xArrLabelrect.width ? xLabelrect.width : xArrLabelrect.width,
height: xLabelrect.height >= xArrLabelrect.height ? xLabelrect.height : xArrLabelrect.height
};
if (rect.width * xaxisLabels.length > w.globals.svgWidth - this.dCtx.lgWidthForSideLegends - this.dCtx.yAxisWidth - this.dCtx.gridPad.left - this.dCtx.gridPad.right && w.config.xaxis.labels.rotate !== 0 || w.config.xaxis.labels.rotateAlways) {
if (!w.globals.isBarHorizontal) {
w.layout.rotateXLabels = true;
const getRotatedTextRects = (text) => {
return graphics.getTextRects(
text,
w.config.xaxis.labels.style.fontSize,
w.config.xaxis.labels.style.fontFamily,
`rotate(${w.config.xaxis.labels.rotate} 0 0)`,
false
);
};
xLabelrect = getRotatedTextRects(val);
if (val !== valArr) {
xArrLabelrect = getRotatedTextRects(valArr);
}
rect.height = (xLabelrect.height > xArrLabelrect.height ? xLabelrect.height : xArrLabelrect.height) / 1.5;
rect.width = xLabelrect.width > xArrLabelrect.width ? xLabelrect.width : xArrLabelrect.width;
}
} else {
w.layout.rotateXLabels = false;
}
}
if (!w.config.xaxis.labels.show) {
rect = {
width: 0,
height: 0
};
}
return {
width: rect.width,
height: rect.height
};
}
/**
* Get X Axis Label Group height
* @memberof Dimensions
* @return {{width: number, height: number}}
*/
getxAxisGroupLabelsCoords() {
var _a;
const w = this.w;
if (!w.labelData.hasXaxisGroups) {
return { width: 0, height: 0 };
}
const fontSize = ((_a = w.config.xaxis.group.style) == null ? void 0 : _a.fontSize) || w.config.xaxis.labels.style.fontSize;
const xaxisLabels = w.labelData.groups.map(
(g) => g.title
);
let rect;
const val = Utils$1.getLargestStringFromArr(xaxisLabels);
const valArr = this.dCtx.dimHelpers.getLargestStringFromMultiArr(
val,
xaxisLabels
);
const graphics = new Graphics(this.w);
const xLabelrect = graphics.getTextRects(val, fontSize);
let xArrLabelrect = xLabelrect;
if (val !== valArr) {
xArrLabelrect = graphics.getTextRects(valArr, fontSize);
}
rect = {
width: xLabelrect.width >= xArrLabelrect.width ? xLabelrect.width : xArrLabelrect.width,
height: xLabelrect.height >= xArrLabelrect.height ? xLabelrect.height : xArrLabelrect.height
};
if (!w.config.xaxis.labels.show) {
rect = {
width: 0,
height: 0
};
}
return {
width: rect.width,
height: rect.height
};
}
/**
* Get X Axis Title Dimensions
* @memberof Dimensions
* @return {{width: number, height: number}}
**/
getxAxisTitleCoords() {
const w = this.w;
let width = 0;
let height = 0;
if (w.config.xaxis.title.text !== void 0) {
const graphics = new Graphics(this.w);
const rect = graphics.getTextRects(
w.config.xaxis.title.text,
w.config.xaxis.title.style.fontSize
);
width = rect.width;
height = rect.height;
}
return {
width,
height
};
}
getxAxisTimeScaleLabelsCoords() {
const w = this.w;
this.dCtx.timescaleLabels = w.labelData.timescaleLabels.slice();
const labels = this.dCtx.timescaleLabels.map(
(label) => label.value
);
const val = labels.reduce((a, b) => {
if (typeof a === "undefined") {
console.error(
"You have possibly supplied invalid Date format. Please supply a valid JavaScript Date"
);
return 0;
} else {
return a.length > b.length ? a : b;
}
}, 0);
const graphics = new Graphics(this.w);
const rect = graphics.getTextRects(
val,
w.config.xaxis.labels.style.fontSize
);
const totalWidthRotated = rect.width * 1.05 * labels.length;
if (totalWidthRotated > w.layout.gridWidth && w.config.xaxis.labels.rotate !== 0) {
w.globals.overlappingXLabels = true;
}
return rect;
}
// In certain cases, the last labels gets cropped in xaxis.
// Hence, we add some additional padding based on the label length to avoid the last label being cropped or we don't draw it at all
/**
* @param {Record<string, any>} xaxisLabelCoords
*/
additionalPaddingXLabels(xaxisLabelCoords) {
const w = this.w;
const gl = w.globals;
const cnf = w.config;
const xtype = cnf.xaxis.type;
const lbWidth = xaxisLabelCoords.width;
gl.skipLastTimelinelabel = false;
gl.skipFirstTimelinelabel = false;
const isBarOpposite = w.config.yaxis[0].opposite && w.globals.isBarHorizontal;
const isCollapsed = (i) => gl.collapsedSeriesIndices.indexOf(i) !== -1;
const rightPad = (yaxe) => {
if (this.dCtx.timescaleLabels && this.dCtx.timescaleLabels.length) {
const firstimescaleLabel = this.dCtx.timescaleLabels[0];
const lastTimescaleLabel = this.dCtx.timescaleLabels[this.dCtx.timescaleLabels.length - 1];
const lastLabelPosition = lastTimescaleLabel.position + lbWidth / 1.75 - this.dCtx.yAxisWidthRight;
const firstLabelPosition = firstimescaleLabel.position - lbWidth / 1.75 + this.dCtx.yAxisWidthLeft;
const lgRightRectWidth = w.config.legend.position === "right" && this.dCtx.lgRect.width > 0 ? this.dCtx.lgRect.width : 0;
if (lastLabelPosition > gl.svgWidth - w.layout.translateX - lgRightRectWidth) {
gl.skipLastTimelinelabel = true;
}
if (firstLabelPosition < -((!yaxe.show || yaxe.floating) && (cnf.chart.type === "bar" || cnf.chart.type === "candlestick" || cnf.chart.type === "rangeBar" || cnf.chart.type === "boxPlot") ? lbWidth / 1.75 : 10)) {
gl.skipFirstTimelinelabel = true;
}
} else if (xtype === "datetime") {
if (this.dCtx.gridPad.right < lbWidth && !w.layout.rotateXLabels) {
gl.skipLastTimelinelabel = true;
}
} else if (xtype !== "datetime") {
if (this.dCtx.gridPad.right < lbWidth / 2 - this.dCtx.yAxisWidthRight && !w.layout.rotateXLabels && !w.config.xaxis.labels.trim) {
this.dCtx.xPadRight = lbWidth / 2 + 1;
}
}
};
const padYAxe = (yaxe, i) => {
if (cnf.yaxis.length > 1 && isCollapsed(i)) return;
rightPad(yaxe);
};
cnf.yaxis.forEach((yaxe, i) => {
if (isBarOpposite) {
if (this.dCtx.gridPad.left < lbWidth) {
this.dCtx.xPadLeft = lbWidth / 2 + 1;
}
this.dCtx.xPadRight = lbWidth / 2 + 1;
} else {
padYAxe(yaxe, i);
}
});
}
}
class DimYAxis {
/**
* @param {import('./Dimensions').default} dCtx
*/
constructor(dCtx) {
this.w = dCtx.w;
this.dCtx = dCtx;
}
/**
* Get Y Axis Dimensions
* @memberof Dimensions
* @returns {Array<{width: number, height: number}>}
**/
getyAxisLabelsCoords() {
const w = this.w;
const width = 0;
const height = 0;
const ret = [];
let labelPad = 10;
const axesUtils = new AxesUtils(this.w, { theme: this.dCtx.theme, timeScale: this.dCtx.timeScale });
w.config.yaxis.map((yaxe, index) => {
const formatterArgs = {
seriesIndex: index,
dataPointIndex: -1,
w
};
const yS = w.globals.yAxisScale[index];
let yAxisMinWidth = 0;
if (!axesUtils.isYAxisHidden(index) && yaxe.labels.show && yaxe.labels.minWidth !== void 0)
yAxisMinWidth = yaxe.labels.minWidth;
if (!axesUtils.isYAxisHidden(index) && yaxe.labels.show && yS.result.length) {
const lbFormatter = w.formatters.yLabelFormatters[index];
const minV = yS.niceMin === Number.MIN_VALUE ? 0 : yS.niceMin;
let val = yS.result.reduce((acc, curr) => {
var _a, _b;
return ((_a = String(lbFormatter(acc, formatterArgs))) == null ? void 0 : _a.length) > ((_b = String(lbFormatter(curr, formatterArgs))) == null ? void 0 : _b.length) ? acc : curr;
}, minV);
val = lbFormatter(val, formatterArgs);
let valArr = val;
if (typeof val === "undefined" || val.length === 0) {
val = yS.niceMax;
}
if (String(val).length === 1) {
val = val + ".0";
valArr = val;
}
if (w.globals.isBarHorizontal) {
labelPad = 0;
const barYaxisLabels = w.labelData.labels.slice();
val = Utils$1.getLargestStringFromArr(barYaxisLabels);
val = lbFormatter(val, { seriesIndex: index, dataPointIndex: -1, w });
valArr = this.dCtx.dimHelpers.getLargestStringFromMultiArr(
val,
barYaxisLabels
);
}
const graphics = new Graphics(this.w);
const rotateStr = "rotate(".concat(yaxe.labels.rotate, " 0 0)");
const rect = graphics.getTextRects(
val,
yaxe.labels.style.fontSize,
yaxe.labels.style.fontFamily,
rotateStr,
false
);
let arrLabelrect = rect;
if (val !== valArr) {
arrLabelrect = graphics.getTextRects(
valArr,
yaxe.labels.style.fontSize,
yaxe.labels.style.fontFamily,
rotateStr,
false
);
}
ret.push({
width: (yAxisMinWidth > arrLabelrect.width || yAxisMinWidth > rect.width ? yAxisMinWidth : arrLabelrect.width > rect.width ? arrLabelrect.width : rect.width) + labelPad,
height: arrLabelrect.height > rect.height ? arrLabelrect.height : rect.height
});
} else {
ret.push({
width,
height
});
}
});
return ret;
}
/**
* Get Y Axis Dimensions
* @memberof Dimensions
* @returns {Array<{width: number, height: number}>}
**/
getyAxisTitleCoords() {
const w = this.w;
const ret = [];
w.config.yaxis.map((yaxe) => {
if (yaxe.show && yaxe.title.text !== void 0) {
const graphics = new Graphics(this.w);
const rotateStr = "rotate(".concat(yaxe.title.rotate, " 0 0)");
const rect = graphics.getTextRects(
yaxe.title.text,
yaxe.title.style.fontSize,
yaxe.title.style.fontFamily,
rotateStr,
false
);
ret.push({
width: rect.width,
height: rect.height
});
} else {
ret.push({
width: 0,
height: 0
});
}
});
return ret;
}
getTotalYAxisWidth() {
const w = this.w;
let yAxisWidth = 0;
let yAxisWidthLeft = 0;
let yAxisWidthRight = 0;
const padding = w.globals.yAxisScale.length > 1 ? 10 : 0;
const axesUtils = new AxesUtils(this.w, { theme: this.dCtx.theme, timeScale: this.dCtx.timeScale });
const isHiddenYAxis = function(index) {
return w.globals.ignoreYAxisIndexes.indexOf(index) > -1;
};
const padForLabelTitle = (coord, index) => {
const floating = w.config.yaxis[index].floating;
let width = 0;
if (coord.width > 0 && !floating) {
width = coord.width + padding;
if (isHiddenYAxis(index)) {
width = width - coord.width - padding;
}
} else {
width = floating || axesUtils.isYAxisHidden(index) ? 0 : 5;
}
w.config.yaxis[index].opposite ? yAxisWidthRight = yAxisWidthRight + width : yAxisWidthLeft = yAxisWidthLeft + width;
yAxisWidth = yAxisWidth + width;
};
w.layout.yLabelsCoords.map((yLabelCoord, index) => {
padForLabelTitle(yLabelCoord, index);
});
w.layout.yTitleCoords.map((yTitleCoord, index) => {
padForLabelTitle(yTitleCoord, index);
});
if (w.globals.isBarHorizontal && !w.config.yaxis[0].floating) {
yAxisWidth = w.layout.yLabelsCoords[0].width + w.layout.yTitleCoords[0].width + 15;
}
this.dCtx.yAxisWidthLeft = yAxisWidthLeft;
this.dCtx.yAxisWidthRight = yAxisWidthRight;
return yAxisWidth;
}
}
class DimGrid {
/**
* @param {import('./Dimensions').default} dCtx
*/
constructor(dCtx) {
this.w = dCtx.w;
this.dCtx = dCtx;
}
/**
* @param {number} gridWidth
*/
gridPadForColumnsInNumericAxis(gridWidth) {
const { w } = this;
const { config: cnf, globals: gl } = w;
if (gl.noData || gl.collapsedSeries.length + gl.ancillaryCollapsedSeries.length === cnf.series.length) {
return 0;
}
const hasBar = (type2) => ["bar", "rangeBar", "candlestick", "boxPlot"].includes(type2);
const type = cnf.chart.type;
let barWidth = 0;
let seriesLen = hasBar(type) ? cnf.series.length : 1;
if (gl.comboBarCount > 0) {
seriesLen = gl.comboBarCount;
}
gl.collapsedSeries.forEach((c) => {
if (hasBar(c.type)) {
seriesLen -= 1;
}
});
if (cnf.chart.stacked) {
seriesLen = 1;
}
const barsPresent = hasBar(type) || gl.comboBarCount > 0;
let xRange = Math.abs(gl.initialMaxX - gl.initialMinX);
if (barsPresent && w.axisFlags.isXNumeric && !gl.isBarHorizontal && seriesLen > 0 && xRange !== 0) {
if (xRange <= 3) {
xRange = gl.dataPoints;
}
const xRatio = xRange / gridWidth;
let xDivision = gl.minXDiff && gl.minXDiff / xRatio > 0 ? gl.minXDiff / xRatio : 0;
if (xDivision > gridWidth / 2) {
xDivision /= 2;
}
barWidth = xDivision * parseInt(cnf.plotOptions.bar.columnWidth, 10) / 100;
if (barWidth < 1) {
barWidth = 1;
}
gl.barPadForNumericAxis = barWidth;
}
return barWidth;
}
gridPadFortitleSubtitle() {
const { w } = this;
const { globals: gl } = w;
let gridShrinkOffset = this.dCtx.isSparkline || !gl.axisCharts ? 0 : 10;
const titleSubtitle = ["title", "subtitle"];
titleSubtitle.forEach((t) => {
if (w.config[t].text !== void 0) {
gridShrinkOffset += w.config[t].margin;
} else {
gridShrinkOffset += this.dCtx.isSparkline || !gl.axisCharts ? 0 : 5;
}
});
if (w.config.legend.show && w.config.legend.position === "bottom" && !w.config.legend.floating && !gl.axisCharts) {
gridShrinkOffset += 10;
}
const titleCoords = this.dCtx.dimHelpers.getTitleSubtitleCoords("title");
const subtitleCoords = this.dCtx.dimHelpers.getTitleSubtitleCoords("subtitle");
w.layout.gridHeight -= titleCoords.height + subtitleCoords.height + gridShrinkOffset;
w.layout.translateY += titleCoords.height + subtitleCoords.height + gridShrinkOffset;
}
/**
* @param {{width: number, height: number}[]} yTitleCoords
* @param {{width: number, height: number}[]} yaxisLabelCoords
*/
setGridXPosForDualYAxis(yTitleCoords, yaxisLabelCoords) {
const { w } = this;
const axesUtils = new AxesUtils(this.w, { theme: this.dCtx.theme, timeScale: this.dCtx.timeScale });
w.config.yaxis.forEach((yaxe, index) => {
if (w.globals.ignoreYAxisIndexes.indexOf(index) === -1 && !yaxe.floating && !axesUtils.isYAxisHidden(index)) {
if (yaxe.opposite) {
w.layout.translateX -= yaxisLabelCoords[index].width + yTitleCoords[index].width + parseInt(yaxe.labels.style.fontSize, 10) / 1.2 + 12;
}
if (w.layout.translateX < 2) {
w.layout.translateX = 2;
}
}
});
}
}
class Dimensions {
/**
* @param {import('../../types/internal').ChartStateW} w
* @param {import('../../types/internal').ChartContext} ctx
*/
constructor(w, ctx) {
this.w = w;
this.ctx = ctx;
this.theme = ctx.theme;
this.timeScale = ctx.timeScale;
this.lgRect = /** @type {any} */
{};
this.yAxisWidth = 0;
this.yAxisWidthLeft = 0;
this.yAxisWidthRight = 0;
this.xAxisHeight = 0;
this.isSparkline = this.w.config.chart.sparkline.enabled;
this.dimHelpers = new Helpers$4(this);
this.dimYAxis = new DimYAxis(this);
this.dimXAxis = new DimXAxis(this);
this.dimGrid = new DimGrid(this);
this.lgWidthForSideLegends = 0;
this.gridPad = this.w.config.grid.padding;
this.xPadRight = 0;
this.xPadLeft = 0;
this.datalabelsCoords = { width: 0, height: 0 };
this.xAxisWidth = 0;
this.timescaleLabels = [];
}
/**
* @memberof Dimensions
**/
plotCoords() {
const w = this.w;
const gl = w.globals;
this.lgRect = this.dimHelpers.getLegendsRect();
this.datalabelsCoords = { width: 0, height: 0 };
const maxStrokeWidth = Array.isArray(w.config.stroke.width) ? Math.max(...w.config.stroke.width) : w.config.stroke.width;
if (this.isSparkline) {
if (w.config.markers.discrete.length > 0 || w.config.markers.size > 0) {
Object.entries(this.gridPad).forEach(([k, v]) => {
this.gridPad[k] = Math.max(
v,
this.w.globals.markers.largestSize / 1.5
);
});
}
this.gridPad.top = Math.max(maxStrokeWidth / 2, this.gridPad.top);
this.gridPad.bottom = Math.max(maxStrokeWidth / 2, this.gridPad.bottom);
}
if (gl.axisCharts) {
this.setDimensionsForAxisCharts();
} else {
this.setDimensionsForNonAxisCharts();
}
this.dimGrid.gridPadFortitleSubtitle();
w.layout.gridHeight = w.layout.gridHeight - this.gridPad.top - this.gridPad.bottom;
w.layout.gridWidth = w.layout.gridWidth - this.gridPad.left - this.gridPad.right - this.xPadRight - this.xPadLeft;
const barWidth = this.dimGrid.gridPadForColumnsInNumericAxis(
w.layout.gridWidth
);
w.layout.gridWidth = w.layout.gridWidth - barWidth * 2;
w.layout.translateX = w.layout.translateX + this.gridPad.left + this.xPadLeft + (barWidth > 0 ? barWidth : 0);
w.layout.translateY = w.layout.translateY + this.gridPad.top;
return {
// w.layout (future slice)
layout: {
gridHeight: w.layout.gridHeight,
gridWidth: w.layout.gridWidth,
translateX: w.layout.translateX,
translateY: w.layout.translateY,
translateXAxisX: w.layout.translateXAxisX,
translateXAxisY: w.layout.translateXAxisY,
rotateXLabels: w.layout.rotateXLabels,
xAxisHeight: w.layout.xAxisHeight,
xAxisLabelsHeight: w.layout.xAxisLabelsHeight,
xAxisGroupLabelsHeight: w.layout.xAxisGroupLabelsHeight,
xAxisLabelsWidth: w.layout.xAxisLabelsWidth,
yLabelsCoords: w.layout.yLabelsCoords,
yTitleCoords: w.layout.yTitleCoords
}
};
}
setDimensionsForAxisCharts() {
const w = this.w;
const gl = w.globals;
const yaxisLabelCoords = this.dimYAxis.getyAxisLabelsCoords();
const yTitleCoords = this.dimYAxis.getyAxisTitleCoords();
if (gl.isSlopeChart) {
this.datalabelsCoords = this.dimHelpers.getDatalabelsRect();
}
w.layout.yLabelsCoords = [];
w.layout.yTitleCoords = [];
w.config.yaxis.map((yaxe, index) => {
w.layout.yLabelsCoords.push({
width: yaxisLabelCoords[index].width,
index
});
w.layout.yTitleCoords.push(
/** @type {any} */
{
width: yTitleCoords[index].width,
index
}
);
});
this.yAxisWidth = this.dimYAxis.getTotalYAxisWidth();
const xaxisLabelCoords = this.dimXAxis.getxAxisLabelsCoords();
const xaxisGroupLabelCoords = this.dimXAxis.getxAxisGroupLabelsCoords();
const xtitleCoords = this.dimXAxis.getxAxisTitleCoords();
this.conditionalChecksForAxisCoords(
xaxisLabelCoords,
xtitleCoords,
xaxisGroupLabelCoords
);
w.layout.translateXAxisY = w.layout.rotateXLabels ? this.xAxisHeight / 8 : -4;
w.layout.translateXAxisX = w.layout.rotateXLabels && w.axisFlags.isXNumeric && w.config.xaxis.labels.rotate <= -45 ? -this.xAxisWidth / 4 : 0;
if (w.globals.isBarHorizontal) {
w.layout.rotateXLabels = false;
w.layout.translateXAxisY = -1 * (parseInt(w.config.xaxis.labels.style.fontSize, 10) / 1.5);
}
w.layout.translateXAxisY = w.layout.translateXAxisY + w.config.xaxis.labels.offsetY;
w.layout.translateXAxisX = w.layout.translateXAxisX + w.config.xaxis.labels.offsetX;
let yAxisWidth = this.yAxisWidth;
let xAxisHeight = this.xAxisHeight;
w.layout.xAxisLabelsHeight = this.xAxisHeight - xtitleCoords.height;
w.layout.xAxisGroupLabelsHeight = w.layout.xAxisLabelsHeight - xaxisLabelCoords.height;
w.layout.xAxisLabelsWidth = this.xAxisWidth;
w.layout.xAxisHeight = this.xAxisHeight;
let translateY = 10;
if (w.config.chart.type === "radar" || this.isSparkline) {
yAxisWidth = 0;
xAxisHeight = 0;
}
if (this.isSparkline) {
this.lgRect = {
height: 0,
width: 0
};
}
if (this.isSparkline || w.config.chart.type === "treemap") {
yAxisWidth = 0;
xAxisHeight = 0;
translateY = 0;
}
if (!this.isSparkline && w.config.chart.type !== "treemap") {
this.dimXAxis.additionalPaddingXLabels(xaxisLabelCoords);
}
const legendTopBottom = () => {
w.layout.translateX = yAxisWidth + this.datalabelsCoords.width;
w.layout.gridHeight = gl.svgHeight - this.lgRect.height - xAxisHeight - (!this.isSparkline && w.config.chart.type !== "treemap" ? w.layout.rotateXLabels ? 10 : 15 : 0);
w.layout.gridWidth = gl.svgWidth - yAxisWidth - this.datalabelsCoords.width * 2;
};
if (w.config.xaxis.position === "top")
translateY = w.layout.xAxisHeight - w.config.xaxis.axisTicks.height - 5;
switch (w.config.legend.position) {
case "bottom":
w.layout.translateY = translateY;
legendTopBottom();
break;
case "top":
w.layout.translateY = this.lgRect.height + translateY;
legendTopBottom();
break;
case "left":
w.layout.translateY = translateY;
w.layout.translateX = this.lgRect.width + yAxisWidth + this.datalabelsCoords.width;
w.layout.gridHeight = gl.svgHeight - xAxisHeight - 12;
w.layout.gridWidth = gl.svgWidth - this.lgRect.width - yAxisWidth - this.datalabelsCoords.width * 2;
break;
case "right":
w.layout.translateY = translateY;
w.layout.translateX = yAxisWidth + this.datalabelsCoords.width;
w.layout.gridHeight = gl.svgHeight - xAxisHeight - 12;
w.layout.gridWidth = gl.svgWidth - this.lgRect.width - yAxisWidth - this.datalabelsCoords.width * 2 - 5;
break;
default:
throw new Error("Legend position not supported");
}
this.dimGrid.setGridXPosForDualYAxis(yTitleCoords, yaxisLabelCoords);
const objyAxis = new YAxis(this.w, {
theme: this.theme,
timeScale: this.timeScale
});
objyAxis.setYAxisXPosition(yaxisLabelCoords, yTitleCoords);
}
setDimensionsForNonAxisCharts() {
const w = this.w;
const gl = w.globals;
const cnf = w.config;
let xPad = 0;
if (w.config.legend.show && !w.config.legend.floating) {
xPad = 20;
}
const type = cnf.chart.type === "pie" || cnf.chart.type === "polarArea" || cnf.chart.type === "donut" ? "pie" : "radialBar";
const offY = cnf.plotOptions[type].offsetY;
const offX = cnf.plotOptions[type].offsetX;
if (!cnf.legend.show || cnf.legend.floating) {
w.layout.gridHeight = gl.svgHeight;
const maxWidth = w.dom.elWrap.getBoundingClientRect().width;
w.layout.gridWidth = Math.min(maxWidth, w.layout.gridHeight);
w.layout.translateY = offY;
w.layout.translateX = offX + (gl.svgWidth - w.layout.gridWidth) / 2;
return;
}
switch (cnf.legend.position) {
case "bottom":
w.layout.gridHeight = gl.svgHeight - this.lgRect.height;
w.layout.gridWidth = gl.svgWidth;
w.layout.translateY = offY - 10;
w.layout.translateX = offX + (gl.svgWidth - w.layout.gridWidth) / 2;
break;
case "top":
w.layout.gridHeight = gl.svgHeight - this.lgRect.height;
w.layout.gridWidth = gl.svgWidth;
w.layout.translateY = this.lgRect.height + offY + 10;
w.layout.translateX = offX + (gl.svgWidth - w.layout.gridWidth) / 2;
break;
case "left":
w.layout.gridWidth = gl.svgWidth - this.lgRect.width - xPad;
w.layout.gridHeight = cnf.chart.height !== "auto" ? gl.svgHeight : w.layout.gridWidth;
w.layout.translateY = offY;
w.layout.translateX = offX + this.lgRect.width + xPad;
break;
case "right":
w.layout.gridWidth = gl.svgWidth - this.lgRect.width - xPad - 5;
w.layout.gridHeight = cnf.chart.height !== "auto" ? gl.svgHeight : w.layout.gridWidth;
w.layout.translateY = offY;
w.layout.translateX = offX + 10;
break;
default:
throw new Error("Legend position not supported");
}
}
/**
* @param {any} xaxisLabelCoords
* @param {any} xtitleCoords
* @param {any} xaxisGroupLabelCoords
*/
conditionalChecksForAxisCoords(xaxisLabelCoords, xtitleCoords, xaxisGroupLabelCoords) {
const w = this.w;
const xAxisNum = w.labelData.hasXaxisGroups ? 2 : 1;
const baseXAxisHeight = xaxisGroupLabelCoords.height + xaxisLabelCoords.height + xtitleCoords.height;
const xAxisHeightMultiplicate = w.axisFlags.isMultiLineX ? 1.2 : LINE_HEIGHT_RATIO;
const rotatedXAxisOffset = w.layout.rotateXLabels ? 22 : 10;
const rotatedXAxisLegendOffset = w.layout.rotateXLabels && w.config.legend.position === "bottom";
const additionalOffset = rotatedXAxisLegendOffset ? 10 : 0;
this.xAxisHeight = baseXAxisHeight * xAxisHeightMultiplicate + xAxisNum * rotatedXAxisOffset + additionalOffset;
this.xAxisWidth = xaxisLabelCoords.width;
if (this.xAxisHeight - xtitleCoords.height > w.config.xaxis.labels.maxHeight) {
this.xAxisHeight = w.config.xaxis.labels.maxHeight;
}
if (w.config.xaxis.labels.minHeight && this.xAxisHeight < w.config.xaxis.labels.minHeight) {
this.xAxisHeight = w.config.xaxis.labels.minHeight;
}
if (w.config.xaxis.floating) {
this.xAxisHeight = 0;
}
let minYAxisWidth = 0;
let maxYAxisWidth = 0;
w.config.yaxis.forEach((y) => {
minYAxisWidth += y.labels.minWidth;
maxYAxisWidth += y.labels.maxWidth;
});
if (this.yAxisWidth < minYAxisWidth) {
this.yAxisWidth = minYAxisWidth;
}
if (this.yAxisWidth > maxYAxisWidth) {
this.yAxisWidth = maxYAxisWidth;
}
}
}
const MINUTES_IN_DAY = 24 * 60;
const SECONDS_IN_DAY = MINUTES_IN_DAY * 60;
const MIN_ZOOM_DAYS = 10 / SECONDS_IN_DAY;
class TimeScale {
/**
* @param {import('../types/internal').ChartStateW} w
* @param {import('../types/internal').ChartContext} ctx
*/
constructor(w, ctx) {
this.w = w;
this.ctx = ctx;
this.timeScaleArray = [];
this.utc = this.w.config.xaxis.labels.datetimeUTC;
}
/**
* @param {number} minX
* @param {number} maxX
*/
calculateTimeScaleTicks(minX, maxX) {
const w = this.w;
if (w.globals.allSeriesCollapsed) {
w.labelData.labels = [];
w.labelData.timescaleLabels = [];
return [];
}
const dt = new DateTime(this.w);
const daysDiff = (maxX - minX) / (1e3 * SECONDS_IN_DAY);
this.determineInterval(daysDiff);
w.interact.disableZoomIn = false;
w.interact.disableZoomOut = false;
if (daysDiff < MIN_ZOOM_DAYS) {
w.interact.disableZoomIn = true;
} else if (daysDiff > 5e4) {
w.interact.disableZoomOut = true;
}
const timeIntervals = dt.getTimeUnitsfromTimestamp(minX, maxX);
const daysWidthOnXAxis = w.layout.gridWidth / daysDiff;
const hoursWidthOnXAxis = daysWidthOnXAxis / 24;
const minutesWidthOnXAxis = hoursWidthOnXAxis / 60;
const secondsWidthOnXAxis = minutesWidthOnXAxis / 60;
const numberOfHours = Math.floor(daysDiff * 24);
const numberOfMinutes = Math.floor(daysDiff * MINUTES_IN_DAY);
const numberOfSeconds = Math.floor(daysDiff * SECONDS_IN_DAY);
const numberOfDays = Math.floor(daysDiff);
const numberOfMonths = Math.floor(daysDiff / 30);
const numberOfYears = Math.floor(daysDiff / 365);
const firstVal = {
minMillisecond: timeIntervals.minMillisecond,
minSecond: timeIntervals.minSecond,
minMinute: timeIntervals.minMinute,
minHour: timeIntervals.minHour,
minDate: timeIntervals.minDate,
minMonth: timeIntervals.minMonth,
minYear: timeIntervals.minYear
};
const currentMillisecond = firstVal.minMillisecond;
const currentSecond = firstVal.minSecond;
const currentMinute = firstVal.minMinute;
const currentHour = firstVal.minHour;
const currentMonthDate = firstVal.minDate;
const currentDate = firstVal.minDate;
const currentMonth = firstVal.minMonth;
const currentYear = firstVal.minYear;
const params = {
firstVal,
currentMillisecond,
currentSecond,
currentMinute,
currentHour,
currentMonthDate,
currentDate,
currentMonth,
currentYear,
daysWidthOnXAxis,
hoursWidthOnXAxis,
minutesWidthOnXAxis,
secondsWidthOnXAxis,
numberOfSeconds,
numberOfMinutes,
numberOfHours,
numberOfDays,
numberOfMonths,
numberOfYears
};
switch (this.tickInterval) {
case "years": {
this.generateYearScale(params);
break;
}
case "months":
case "half_year": {
this.generateMonthScale(params);
break;
}
case "months_days":
case "months_fortnight":
case "days":
case "week_days": {
this.generateDayScale(params);
break;
}
case "hours": {
this.generateHourScale(params);
break;
}
case "minutes_fives":
case "minutes":
this.generateMinuteScale(params);
break;
case "seconds_tens":
case "seconds_fives":
case "seconds":
this.generateSecondScale(params);
break;
}
const adjustedMonthInTimeScaleArray = this.timeScaleArray.map(
(ts) => {
const defaultReturn = {
position: ts.position,
unit: ts.unit,
year: ts.year,
day: ts.day ? ts.day : 1,
hour: ts.hour ? ts.hour : 0,
month: ts.month + 1
};
if (ts.unit === "month") {
return __spreadProps(__spreadValues({}, defaultReturn), {
day: 1,
value: ts.value + 1
});
} else if (ts.unit === "day" || ts.unit === "hour") {
return __spreadProps(__spreadValues({}, defaultReturn), {
value: ts.value
});
} else if (ts.unit === "minute") {
return __spreadProps(__spreadValues({}, defaultReturn), {
value: ts.value,
minute: ts.value
});
} else if (ts.unit === "second") {
return __spreadProps(__spreadValues({}, defaultReturn), {
value: ts.value,
minute: ts.minute,
second: ts.second
});
}
return ts;
}
);
const filteredTimeScale = adjustedMonthInTimeScaleArray.filter(
(ts) => {
let modulo = 1;
let ticks = Math.ceil(w.layout.gridWidth / 120);
const value = ts.value;
if (w.config.xaxis.tickAmount !== void 0) {
ticks = w.config.xaxis.tickAmount;
}
if (adjustedMonthInTimeScaleArray.length > ticks) {
modulo = Math.floor(adjustedMonthInTimeScaleArray.length / ticks);
}
let shouldNotSkipUnit = false;
let shouldNotPrint = false;
switch (this.tickInterval) {
case "years":
if (ts.unit === "year") {
shouldNotSkipUnit = true;
}
break;
case "half_year":
modulo = 7;
if (ts.unit === "year") {
shouldNotSkipUnit = true;
}
break;
case "months":
modulo = 1;
if (ts.unit === "year") {
shouldNotSkipUnit = true;
}
break;
case "months_fortnight":
modulo = 15;
if (ts.unit === "year" || ts.unit === "month") {
shouldNotSkipUnit = true;
}
if (value === 30) {
shouldNotPrint = true;
}
break;
case "months_days":
modulo = 10;
if (ts.unit === "month") {
shouldNotSkipUnit = true;
}
if (value === 30) {
shouldNotPrint = true;
}
break;
case "week_days":
modulo = 8;
if (ts.unit === "month") {
shouldNotSkipUnit = true;
}
break;
case "days":
modulo = 1;
if (ts.unit === "month") {
shouldNotSkipUnit = true;
}
break;
case "hours":
if (ts.unit === "day") {
shouldNotSkipUnit = true;
}
break;
case "minutes_fives":
if (value % 5 !== 0) {
shouldNotPrint = true;
}
break;
case "seconds_tens":
if (value % 10 !== 0) {
shouldNotPrint = true;
}
break;
case "seconds_fives":
if (value % 5 !== 0) {
shouldNotPrint = true;
}
break;
}
if (this.tickInterval === "hours" || this.tickInterval === "minutes_fives" || this.tickInterval === "seconds_tens" || this.tickInterval === "seconds_fives") {
if (!shouldNotPrint) {
return true;
}
} else {
if ((value % modulo === 0 || shouldNotSkipUnit) && !shouldNotPrint) {
return true;
}
}
}
);
return filteredTimeScale;
}
/**
* @param {Array<Record<string, any>>} filteredTimeScale
*/
recalcDimensionsBasedOnFormat(filteredTimeScale) {
const w = this.w;
const reformattedTimescaleArray = this.formatDates(filteredTimeScale);
const removedOverlappingTS = this.removeOverlappingTS(
reformattedTimescaleArray
);
w.labelData.timescaleLabels = removedOverlappingTS.slice();
const dimensions = new Dimensions(this.w, this.ctx);
const layoutState = dimensions.plotCoords();
this.ctx._writeLayoutCoords(layoutState.layout);
}
/**
* @param {number} daysDiff
*/
determineInterval(daysDiff) {
const yearsDiff = daysDiff / 365;
const hoursDiff = daysDiff * 24;
const minutesDiff = hoursDiff * 60;
const secondsDiff = minutesDiff * 60;
switch (true) {
case yearsDiff > 5:
this.tickInterval = "years";
break;
case daysDiff > 800:
this.tickInterval = "half_year";
break;
case daysDiff > 180:
this.tickInterval = "months";
break;
case daysDiff > 90:
this.tickInterval = "months_fortnight";
break;
case daysDiff > 60:
this.tickInterval = "months_days";
break;
case daysDiff > 30:
this.tickInterval = "week_days";
break;
case daysDiff > 2:
this.tickInterval = "days";
break;
case hoursDiff > 2.4:
this.tickInterval = "hours";
break;
case minutesDiff > 15:
this.tickInterval = "minutes_fives";
break;
case minutesDiff > 5:
this.tickInterval = "minutes";
break;
case minutesDiff > 1:
this.tickInterval = "seconds_tens";
break;
case secondsDiff > 20:
this.tickInterval = "seconds_fives";
break;
default:
this.tickInterval = "seconds";
break;
}
}
/** @param {{firstVal: any, currentMonth: any, currentYear: any, daysWidthOnXAxis: any, numberOfYears: any}} opts */
generateYearScale({
firstVal,
currentMonth,
currentYear,
daysWidthOnXAxis,
numberOfYears
}) {
let firstTickValue = firstVal.minYear;
let firstTickPosition = 0;
const dt = new DateTime(this.w);
const unit = "year";
if (firstVal.minDate > 1 || firstVal.minMonth > 0) {
const remainingDays = dt.determineRemainingDaysOfYear(
firstVal.minYear,
firstVal.minMonth,
firstVal.minDate
);
const remainingDaysOfFirstYear = dt.determineDaysOfYear(firstVal.minYear) - remainingDays + 1;
firstTickPosition = remainingDaysOfFirstYear * daysWidthOnXAxis;
firstTickValue = firstVal.minYear + 1;
this.timeScaleArray.push({
position: firstTickPosition,
value: firstTickValue,
unit,
year: firstTickValue,
month: 1
});
} else if (firstVal.minDate === 1 && firstVal.minMonth === 0) {
this.timeScaleArray.push({
position: firstTickPosition,
value: firstTickValue,
unit,
year: currentYear,
month: Utils$1.monthMod(currentMonth + 1)
});
}
let year = firstTickValue;
let pos = firstTickPosition;
for (let i = 0; i < numberOfYears; i++) {
year++;
pos = dt.determineDaysOfYear(year - 1) * daysWidthOnXAxis + pos;
this.timeScaleArray.push({
position: pos,
value: year,
unit,
year,
month: 1
});
}
}
/** @param {{firstVal: any, currentMonthDate: any, currentMonth: any, currentYear: any, daysWidthOnXAxis: any, numberOfMonths: any}} opts */
generateMonthScale({
firstVal,
currentMonthDate,
currentMonth,
currentYear,
daysWidthOnXAxis,
numberOfMonths
}) {
let firstTickValue = currentMonth;
let firstTickPosition = 0;
const dt = new DateTime(this.w);
let unit = "month";
let yrCounter = 0;
if (firstVal.minDate > 1) {
const remainingDaysOfFirstMonth = dt.determineDaysOfMonths(currentMonth + 1, firstVal.minYear) - currentMonthDate + 1;
firstTickPosition = remainingDaysOfFirstMonth * daysWidthOnXAxis;
firstTickValue = Utils$1.monthMod(currentMonth + 1);
let year = currentYear + yrCounter;
let month2 = Utils$1.monthMod(firstTickValue);
let value = firstTickValue;
if (firstTickValue === 0) {
unit = "year";
value = year;
month2 = 1;
yrCounter += 1;
year = year + yrCounter;
}
this.timeScaleArray.push({
position: firstTickPosition,
value,
unit,
year,
month: month2
});
} else {
this.timeScaleArray.push({
position: firstTickPosition,
value: firstTickValue,
unit,
year: currentYear,
month: Utils$1.monthMod(currentMonth)
});
}
let month = firstTickValue + 1;
let pos = firstTickPosition;
for (let i = 0, j = 1; i < numberOfMonths; i++, j++) {
month = Utils$1.monthMod(month);
if (month === 0) {
unit = "year";
yrCounter += 1;
} else {
unit = "month";
}
const year = this._getYear(currentYear, month, yrCounter);
pos = dt.determineDaysOfMonths(month, year) * daysWidthOnXAxis + pos;
const monthVal = month === 0 ? year : month;
this.timeScaleArray.push({
position: pos,
value: monthVal,
unit,
year,
month: month === 0 ? 1 : month
});
month++;
}
}
/** @param {{firstVal: any, currentMonth: any, currentYear: any, hoursWidthOnXAxis: any, numberOfDays: any}} opts */
generateDayScale({
firstVal,
currentMonth,
currentYear,
hoursWidthOnXAxis,
numberOfDays
}) {
const dt = new DateTime(this.w);
let unit = "day";
let firstTickValue = firstVal.minDate + 1;
let date = firstTickValue;
const changeMonth = (dateVal, month2, year) => {
const monthdays = dt.determineDaysOfMonths(month2 + 1, year);
if (dateVal > monthdays) {
month2 = month2 + 1;
date = 1;
unit = "month";
val = month2;
return month2;
}
return month2;
};
const remainingHours = 24 - firstVal.minHour;
const yrCounter = 0;
let firstTickPosition = remainingHours * hoursWidthOnXAxis;
let val = firstTickValue;
let month = changeMonth(date, currentMonth, currentYear);
if (firstVal.minHour === 0 && firstVal.minDate === 1) {
firstTickPosition = 0;
val = Utils$1.monthMod(firstVal.minMonth);
unit = "month";
date = firstVal.minDate;
} else if (firstVal.minDate !== 1 && firstVal.minHour === 0 && firstVal.minMinute === 0) {
firstTickPosition = 0;
firstTickValue = firstVal.minDate;
date = firstTickValue;
val = firstTickValue;
month = changeMonth(date, currentMonth, currentYear);
if (val !== 1) {
unit = "day";
}
}
this.timeScaleArray.push({
position: firstTickPosition,
value: val,
unit,
year: this._getYear(currentYear, month, yrCounter),
month: Utils$1.monthMod(month),
day: date
});
let pos = firstTickPosition;
for (let i = 0; i < numberOfDays; i++) {
date += 1;
unit = "day";
month = changeMonth(
date,
month,
this._getYear(currentYear, month, yrCounter)
);
const year = this._getYear(currentYear, month, yrCounter);
pos = 24 * hoursWidthOnXAxis + pos;
const value = date === 1 ? Utils$1.monthMod(month) : date;
this.timeScaleArray.push({
position: pos,
value,
unit,
year,
month: Utils$1.monthMod(month),
day: value
});
}
}
/** @param {{firstVal: any, currentDate: any, currentMonth: any, currentYear: any, minutesWidthOnXAxis: any, numberOfHours: any}} opts */
generateHourScale({
firstVal,
currentDate,
currentMonth,
currentYear,
minutesWidthOnXAxis,
numberOfHours
}) {
const dt = new DateTime(this.w);
const yrCounter = 0;
let unit = "hour";
const changeDate = (dateVal, month2) => {
const monthdays = dt.determineDaysOfMonths(month2 + 1, currentYear);
if (dateVal > monthdays) {
date = 1;
month2 = month2 + 1;
}
return { month: month2, date };
};
const changeMonth = (dateVal, month2) => {
const monthdays = dt.determineDaysOfMonths(month2 + 1, currentYear);
if (dateVal > monthdays) {
month2 = month2 + 1;
return month2;
}
return month2;
};
const remainingMins = 60 - (firstVal.minMinute + firstVal.minSecond / 60);
let firstTickPosition = remainingMins * minutesWidthOnXAxis;
let firstTickValue = firstVal.minHour + 1;
let hour = firstTickValue;
if (remainingMins === 60) {
firstTickPosition = 0;
firstTickValue = firstVal.minHour;
hour = firstTickValue;
}
let date = currentDate;
if (hour >= 24) {
hour = 0;
date += 1;
unit = "day";
firstTickValue = date;
}
const checkNextMonth = changeDate(date, currentMonth);
let month = checkNextMonth.month;
month = changeMonth(date, month);
if (unit === "day") {
firstTickValue = date;
}
this.timeScaleArray.push({
position: firstTickPosition,
value: firstTickValue,
unit,
day: date,
hour,
year: currentYear,
month: Utils$1.monthMod(month)
});
hour++;
let pos = firstTickPosition;
for (let i = 0; i < numberOfHours; i++) {
unit = "hour";
if (hour >= 24) {
hour = 0;
date += 1;
unit = "day";
const checkNextMonth2 = changeDate(date, month);
month = checkNextMonth2.month;
month = changeMonth(date, month);
}
const year = this._getYear(currentYear, month, yrCounter);
pos = 60 * minutesWidthOnXAxis + pos;
const val = hour === 0 ? date : hour;
this.timeScaleArray.push({
position: pos,
value: val,
unit,
hour,
day: date,
year,
month: Utils$1.monthMod(month)
});
hour++;
}
}
/** @param {{currentMillisecond: any, currentSecond: any, currentMinute: any, currentHour: any, currentDate: any, currentMonth: any, currentYear: any, minutesWidthOnXAxis: any, secondsWidthOnXAxis: any, numberOfMinutes: any}} opts */
generateMinuteScale({
currentMillisecond,
currentSecond,
currentMinute,
currentHour,
currentDate,
currentMonth,
currentYear,
minutesWidthOnXAxis,
secondsWidthOnXAxis,
numberOfMinutes
}) {
const dt = new DateTime(this.w);
const yrCounter = 0;
const unit = "minute";
const remainingSecs = 60 - currentSecond;
let firstTickPosition = (remainingSecs - currentMillisecond / 1e3) * secondsWidthOnXAxis;
let minute = currentMinute + 1;
if (currentSecond === 0 && currentMillisecond === 0) {
firstTickPosition = 0;
minute = currentMinute;
}
let date = currentDate;
let month = currentMonth;
const year = currentYear;
let hour = currentHour;
let pos = firstTickPosition;
for (let i = 0; i < numberOfMinutes; i++) {
if (minute >= 60) {
minute = 0;
hour += 1;
if (hour === 24) {
hour = 0;
date += 1;
const monthDays = dt.determineDaysOfMonths(
month + 1,
this._getYear(year, month, yrCounter)
);
if (date > monthDays) {
date = 1;
month += 1;
}
}
}
this.timeScaleArray.push({
position: pos,
value: minute,
unit,
hour,
minute,
day: date,
year: this._getYear(year, month, yrCounter),
month: Utils$1.monthMod(month)
});
pos += minutesWidthOnXAxis;
minute++;
}
}
/** @param {{currentMillisecond: any, currentSecond: any, currentMinute: any, currentHour: any, currentDate: any, currentMonth: any, currentYear: any, secondsWidthOnXAxis: any, numberOfSeconds: any}} opts */
generateSecondScale({
currentMillisecond,
currentSecond,
currentMinute,
currentHour,
currentDate,
currentMonth,
currentYear,
secondsWidthOnXAxis,
numberOfSeconds
}) {
const yrCounter = 0;
const unit = "second";
const remainingMillisecs = 1e3 - currentMillisecond;
let firstTickPosition = remainingMillisecs / 1e3 * secondsWidthOnXAxis;
let second = currentSecond + 1;
if (currentMillisecond === 0) {
firstTickPosition = 0;
second = currentSecond;
}
let minute = currentMinute;
const date = currentDate;
const month = currentMonth;
const year = currentYear;
let hour = currentHour;
let pos = firstTickPosition;
for (let i = 0; i < numberOfSeconds; i++) {
if (second >= 60) {
minute++;
second = 0;
if (minute >= 60) {
hour++;
minute = 0;
if (hour === 24) {
hour = 0;
}
}
}
this.timeScaleArray.push({
position: pos,
value: second,
unit,
hour,
minute,
second,
day: date,
year: this._getYear(year, month, yrCounter),
month: Utils$1.monthMod(month)
});
pos += secondsWidthOnXAxis;
second++;
}
}
/**
* @param {Record<string, any>} ts
* @param {string | number} value
*/
createRawDateString(ts, value) {
let raw = ts.year;
if (ts.month === 0) {
ts.month = 1;
}
raw += "-" + ("0" + ts.month.toString()).slice(-2);
if (ts.unit === "day") {
raw += "-" + ("0" + value).slice(-2);
} else {
raw += "-" + ("0" + (ts.day ? ts.day : "1")).slice(-2);
}
if (ts.unit === "hour") {
raw += "T" + ("0" + value).slice(-2);
} else {
raw += "T" + ("0" + (ts.hour ? ts.hour : "0")).slice(-2);
}
if (ts.unit === "minute") {
raw += ":" + ("0" + value).slice(-2);
} else {
raw += ":" + (ts.minute ? ("0" + ts.minute).slice(-2) : "00");
}
if (ts.unit === "second") {
raw += ":" + ("0" + value).slice(-2);
} else {
raw += ":00";
}
if (this.utc) {
raw += ".000Z";
}
return raw;
}
/**
* @param {Array<Record<string, any>>} filteredTimeScale
*/
formatDates(filteredTimeScale) {
const w = this.w;
const reformattedTimescaleArray = filteredTimeScale.map(
(ts) => {
let value = ts.value.toString();
const dt = new DateTime(this.w);
const raw = this.createRawDateString(ts, value);
let dateToFormat = dt.getDate(dt.parseDate(raw));
if (!this.utc) {
dateToFormat = dt.getDate(dt.parseDateWithTimezone(raw));
}
if (w.config.xaxis.labels.format === void 0) {
let customFormat = "dd MMM";
const dtFormatter = w.config.xaxis.labels.datetimeFormatter;
if (ts.unit === "year") customFormat = dtFormatter.year;
if (ts.unit === "month") customFormat = dtFormatter.month;
if (ts.unit === "day") customFormat = dtFormatter.day;
if (ts.unit === "hour") customFormat = dtFormatter.hour;
if (ts.unit === "minute") customFormat = dtFormatter.minute;
if (ts.unit === "second") customFormat = dtFormatter.second;
value = dt.formatDate(dateToFormat, customFormat);
} else {
value = dt.formatDate(dateToFormat, w.config.xaxis.labels.format);
}
return {
dateString: raw,
position: ts.position,
value,
unit: ts.unit,
year: ts.year,
month: ts.month
};
}
);
return reformattedTimescaleArray;
}
/**
* @param {any[]} arr
*/
removeOverlappingTS(arr) {
const graphics = new Graphics(this.w);
let equalLabelLengthFlag = false;
let constantLabelWidth;
if (arr.length > 0 && // check arr length
arr[0].value && // check arr[0] contains value
/**
* @param {Record<string, any>} lb
*/
arr.every((lb) => lb.value.length === arr[0].value.length)) {
equalLabelLengthFlag = true;
constantLabelWidth = graphics.getTextRects(
arr[0].value,
this.w.config.xaxis.labels.style.fontSize
).width;
}
let lastDrawnIndex = 0;
let filteredArray = arr.map((item, index) => {
if (index > 0 && this.w.config.xaxis.labels.hideOverlappingLabels) {
const prevLabelWidth = !equalLabelLengthFlag ? graphics.getTextRects(
/** @type {any} */
arr[lastDrawnIndex].value,
this.w.config.xaxis.labels.style.fontSize
).width : constantLabelWidth;
const prevPos = arr[lastDrawnIndex].position;
const pos = item.position;
if (pos > prevPos + prevLabelWidth + 10) {
lastDrawnIndex = index;
return item;
} else {
return null;
}
} else {
return item;
}
});
filteredArray = filteredArray.filter((f) => f !== null);
return filteredArray;
}
/**
* @param {number} currentYear
* @param {number} month
* @param {number} yrCounter
*/
_getYear(currentYear, month, yrCounter) {
return currentYear + Math.floor(month / 12) + yrCounter;
}
}
const REGISTRY_KEY = "__apexcharts_registry__";
if (!/** @type {any} */
globalThis[REGISTRY_KEY]) {
globalThis[REGISTRY_KEY] = {};
}
function getRegistry() {
return (
/** @type {any} */
globalThis[REGISTRY_KEY]
);
}
function register(typeMap) {
Object.assign(getRegistry(), typeMap);
}
function getChartClass(type) {
const Cls = getRegistry()[type];
if (!Cls) {
throw new Error(
`ApexCharts: chart type "${type}" is not registered. Import it via ApexCharts.use() or use the full apexcharts bundle.`
);
}
return Cls;
}
class Core {
/**
* @param {Element} el
* @param {import('../types/internal').ChartStateW} w
* @param {import('../types/internal').ChartContext} ctx
*/
constructor(el, w, ctx) {
this.w = w;
this.ctx = ctx;
this.el = el;
}
setupElements() {
const { globals: gl, config: cnf } = this.w;
const ct = cnf.chart.type;
const xyChartsArrTypes = [
"line",
"area",
"bar",
"rangeBar",
"rangeArea",
"candlestick",
"boxPlot",
"scatter",
"bubble"
];
const axisChartsArrTypes = [
...xyChartsArrTypes,
"radar",
"heatmap",
"treemap"
];
gl.axisCharts = axisChartsArrTypes.includes(ct);
gl.xyCharts = xyChartsArrTypes.includes(ct);
gl.isBarHorizontal = ["bar", "rangeBar", "boxPlot"].includes(ct) && cnf.plotOptions.bar.horizontal;
gl.chartClass = `.apexcharts${gl.chartID}`;
this.w.dom.baseEl = this.el;
this.w.dom.elWrap = BrowserAPIs.createElementNS(
"http://www.w3.org/1999/xhtml",
"div"
);
Graphics.setAttrs(this.w.dom.elWrap, {
id: gl.chartClass.substring(1),
class: `apexcharts-canvas ${gl.chartClass.substring(1)}`
});
this.el.appendChild(this.w.dom.elWrap);
const SVG2 = Environment.isBrowser() ? (
/** @type {any} */
window.SVG
) : (
/** @type {any} */
global.SVG
);
this.w.dom.Paper = SVG2().addTo(this.w.dom.elWrap);
this.w.dom.Paper.attr({
class: "apexcharts-svg",
"xmlns:data": "ApexChartsNS",
transform: `translate(${cnf.chart.offsetX}, ${cnf.chart.offsetY})`
});
this.w.dom.Paper.node.style.background = cnf.theme.mode === "dark" && !cnf.chart.background ? "#343A3F" : cnf.theme.mode === "light" && !cnf.chart.background ? "#fff" : cnf.chart.background;
this.setSVGDimensions();
this.w.dom.elLegendForeign = BrowserAPIs.createElementNS(
SVGNS,
"foreignObject"
);
Graphics.setAttrs(this.w.dom.elLegendForeign, {
x: 0,
y: 0,
width: gl.svgWidth,
height: gl.svgHeight
});
this.w.dom.elLegendWrap = BrowserAPIs.createElementNS(
"http://www.w3.org/1999/xhtml",
"div"
);
this.w.dom.elLegendWrap.classList.add("apexcharts-legend");
this.w.dom.elWrap.appendChild(this.w.dom.elLegendWrap);
this.w.dom.Paper.node.appendChild(this.w.dom.elLegendForeign);
if (cnf.chart.accessibility.enabled && cnf.chart.accessibility.announcements.enabled) {
const srStatus = BrowserAPIs.createElement("div");
srStatus.className = "apexcharts-sr-status";
srStatus.setAttribute("role", "status");
srStatus.setAttribute("aria-live", "polite");
srStatus.setAttribute("aria-atomic", "true");
this.w.dom.elWrap.appendChild(srStatus);
}
if (cnf.chart.accessibility.enabled) {
const ariaLabel = this.getAccessibleChartLabel();
const svgRole = cnf.chart.accessibility.keyboard.enabled && cnf.chart.accessibility.keyboard.navigation.enabled ? "application" : "img";
this.w.dom.Paper.attr({
role: svgRole,
"aria-label": ariaLabel
});
const titleEl = BrowserAPIs.createElementNS(SVGNS, "title");
titleEl.textContent = ariaLabel;
this.w.dom.Paper.node.insertBefore(
titleEl,
this.w.dom.elLegendForeign.nextSibling
);
if (cnf.chart.accessibility.description) {
const descEl = BrowserAPIs.createElementNS(SVGNS, "desc");
descEl.textContent = cnf.chart.accessibility.description;
this.w.dom.Paper.node.insertBefore(
descEl,
this.w.dom.elLegendForeign.nextSibling
);
}
}
this.w.dom.elGraphical = this.w.dom.Paper.group().attr({
class: "apexcharts-inner apexcharts-graphical"
});
this.w.dom.elDefs = this.w.dom.Paper.defs();
this.w.dom.Paper.add(this.w.dom.elGraphical);
this.w.dom.elGraphical.add(this.w.dom.elDefs);
}
/**
* @param {any[]} ser
* @param {import('../types/internal').XYRatios} xyRatios
*/
plotChartType(ser, xyRatios) {
const { w, ctx } = this;
const { config: cnf, globals: gl } = w;
const seriesTypes = {
line: { series: [], i: [] },
area: { series: [], i: [] },
scatter: { series: [], i: [] },
bubble: { series: [], i: [] },
bar: { series: [], i: [] },
candlestick: { series: [], i: [] },
boxPlot: { series: [], i: [] },
rangeBar: { series: [], i: [] },
rangeArea: { series: [], seriesRangeEnd: [], i: [] }
};
const chartType = cnf.chart.type || "line";
let nonComboType = null;
let comboCount = 0;
this.w.seriesData.series.forEach((serie, st) => {
var _a, _b;
const seriesType = ((_a = ser[st]) == null ? void 0 : _a.type) === "column" ? "bar" : ((_b = ser[st]) == null ? void 0 : _b.type) || (chartType === "column" ? "bar" : chartType);
if (
/** @type {Record<string,any>} */
seriesTypes[seriesType]
) {
if (seriesType === "rangeArea") {
seriesTypes[seriesType].series.push(this.w.rangeData.seriesRangeStart[st]);
seriesTypes[seriesType].seriesRangeEnd.push(this.w.rangeData.seriesRangeEnd[st]);
} else {
seriesTypes[seriesType].series.push(serie);
}
seriesTypes[seriesType].i.push(st);
if (seriesType === "bar") w.globals.columnSeries = seriesTypes.bar;
} else if ([
"heatmap",
"treemap",
"pie",
"donut",
"polarArea",
"radialBar",
"radar"
].includes(seriesType)) {
nonComboType = seriesType;
} else {
console.warn(
`You have specified an unrecognized series type (${seriesType}).`
);
}
if (chartType !== seriesType && seriesType !== "scatter") comboCount++;
});
if (comboCount > 0) {
if (nonComboType) {
console.warn(
`Chart or series type ${nonComboType} cannot appear with other chart or series types.`
);
}
if (seriesTypes.bar.series.length > 0 && cnf.plotOptions.bar.horizontal) {
comboCount -= seriesTypes.bar.series.length;
seriesTypes.bar = { series: [], i: [] };
w.globals.columnSeries = { series: [], i: [] };
console.warn(
"Horizontal bars are not supported in a mixed/combo chart. Please turn off `plotOptions.bar.horizontal`"
);
}
}
gl.comboCharts || (gl.comboCharts = comboCount > 0);
const needsLine = seriesTypes.line.series.length > 0 || seriesTypes.area.series.length > 0 || seriesTypes.scatter.series.length > 0 || seriesTypes.bubble.series.length > 0 || seriesTypes.rangeArea.series.length > 0 || !gl.comboCharts && ["line", "area", "scatter", "bubble", "rangeArea"].includes(
cnf.chart.type
);
const line = needsLine ? new (getChartClass("line"))(ctx.w, ctx, xyRatios) : null;
const needsCandlestick = seriesTypes.candlestick.series.length > 0 || seriesTypes.boxPlot.series.length > 0 || !gl.comboCharts && ["candlestick", "boxPlot"].includes(cnf.chart.type);
const boxCandlestick = needsCandlestick ? new (getChartClass("candlestick"))(ctx.w, ctx, xyRatios) : null;
const needsPie = !gl.comboCharts && ["pie", "donut", "polarArea"].includes(cnf.chart.type);
ctx.pie = needsPie ? new (getChartClass("pie"))(ctx.w, ctx) : null;
const needsRangeBar = seriesTypes.rangeBar.series.length > 0 || !gl.comboCharts && cnf.chart.type === "rangeBar";
ctx.rangeBar = needsRangeBar ? new (getChartClass("rangeBar"))(ctx.w, ctx, xyRatios) : null;
let elGraph = [];
if (gl.comboCharts) {
const coreUtils = new CoreUtils(this.w);
if (seriesTypes.area.series.length > 0) {
elGraph.push(
...coreUtils.drawSeriesByGroup(
seriesTypes.area,
gl.areaGroups,
"area",
line
)
);
}
if (seriesTypes.bar.series.length > 0) {
if (cnf.chart.stacked) {
const barStacked = new (getChartClass("barStacked"))(
ctx.w,
ctx,
xyRatios
);
elGraph.push(
barStacked.draw(seriesTypes.bar.series, seriesTypes.bar.i)
);
} else {
ctx.bar = new (getChartClass("bar"))(ctx.w, ctx, xyRatios);
elGraph.push(ctx.bar.draw(seriesTypes.bar.series, seriesTypes.bar.i));
}
}
if (seriesTypes.rangeArea.series.length > 0) {
elGraph.push(
line.draw(
seriesTypes.rangeArea.series,
"rangeArea",
seriesTypes.rangeArea.i,
seriesTypes.rangeArea.seriesRangeEnd
)
);
}
if (seriesTypes.line.series.length > 0) {
elGraph.push(
...coreUtils.drawSeriesByGroup(
seriesTypes.line,
gl.lineGroups,
"line",
line
)
);
}
if (seriesTypes.candlestick.series.length > 0) {
elGraph.push(
boxCandlestick.draw(
seriesTypes.candlestick.series,
"candlestick",
seriesTypes.candlestick.i
)
);
}
if (seriesTypes.boxPlot.series.length > 0) {
elGraph.push(
boxCandlestick.draw(
seriesTypes.boxPlot.series,
"boxPlot",
seriesTypes.boxPlot.i
)
);
}
if (seriesTypes.rangeBar.series.length > 0) {
elGraph.push(
ctx.rangeBar.draw(
seriesTypes.rangeBar.series,
seriesTypes.rangeBar.i
)
);
}
if (seriesTypes.scatter.series.length > 0) {
const scatterLine = new (getChartClass("line"))(
ctx.w,
ctx,
xyRatios,
true
);
elGraph.push(
scatterLine.draw(
seriesTypes.scatter.series,
"scatter",
seriesTypes.scatter.i
)
);
}
if (seriesTypes.bubble.series.length > 0) {
const bubbleLine = new (getChartClass("line"))(
ctx.w,
ctx,
xyRatios,
true
);
elGraph.push(
bubbleLine.draw(
seriesTypes.bubble.series,
"bubble",
seriesTypes.bubble.i
)
);
}
} else {
const type = cnf.chart.type;
switch (type) {
case "line":
elGraph = line.draw(this.w.seriesData.series, "line");
break;
case "area":
elGraph = line.draw(this.w.seriesData.series, "area");
break;
case "bar":
if (cnf.chart.stacked) {
const barStacked = new (getChartClass("barStacked"))(
ctx.w,
ctx,
xyRatios
);
elGraph = barStacked.draw(this.w.seriesData.series);
} else {
ctx.bar = new (getChartClass("bar"))(ctx.w, ctx, xyRatios);
elGraph = ctx.bar.draw(this.w.seriesData.series);
}
break;
case "candlestick":
elGraph = boxCandlestick.draw(this.w.seriesData.series, "candlestick");
break;
case "boxPlot":
elGraph = boxCandlestick.draw(this.w.seriesData.series, type);
break;
case "rangeBar":
elGraph = ctx.rangeBar.draw(this.w.seriesData.series);
break;
case "rangeArea":
elGraph = line.draw(
this.w.rangeData.seriesRangeStart,
"rangeArea",
void 0,
this.w.rangeData.seriesRangeEnd
);
break;
case "heatmap": {
const heatmap = new (getChartClass("heatmap"))(ctx.w, ctx, xyRatios);
elGraph = heatmap.draw(this.w.seriesData.series);
break;
}
case "treemap": {
const treemap = new (getChartClass("treemap"))(ctx.w, ctx);
elGraph = treemap.draw(this.w.seriesData.series);
break;
}
case "pie":
case "donut":
case "polarArea":
elGraph = ctx.pie.draw(this.w.seriesData.series);
break;
case "radialBar": {
const radialBar = new (getChartClass("radialBar"))(ctx.w, ctx);
elGraph = radialBar.draw(this.w.seriesData.series);
break;
}
case "radar": {
const radar = new (getChartClass("radar"))(ctx.w, ctx);
elGraph = radar.draw(this.w.seriesData.series);
break;
}
default:
elGraph = line.draw(this.w.seriesData.series);
}
}
return elGraph;
}
setSVGDimensions() {
var _a;
const { globals: gl, config: cnf } = this.w;
cnf.chart.width = cnf.chart.width || "100%";
cnf.chart.height = cnf.chart.height || "auto";
const rawWidth = cnf.chart.width;
const rawHeight = cnf.chart.height;
gl.svgWidth = NaN;
gl.svgHeight = NaN;
let elDim = Utils$1.getDimensions(this.el);
const widthUnit = rawWidth.toString().split(/[0-9]+/g).pop();
if (widthUnit === "%") {
if (Utils$1.isNumber(elDim[0])) {
if (elDim[0].width === 0) {
elDim = Utils$1.getDimensions(this.el.parentNode);
}
gl.svgWidth = elDim[0] * parseInt(rawWidth, 10) / 100;
}
} else if (widthUnit === "px" || widthUnit === "") {
gl.svgWidth = parseInt(rawWidth, 10);
}
const heightUnit = String(rawHeight).toString().split(/[0-9]+/g).pop();
if (rawHeight !== "auto" && rawHeight !== "") {
if (heightUnit === "%") {
const elParentDim = Utils$1.getDimensions(this.el.parentNode);
gl.svgHeight = elParentDim[1] * parseInt(rawHeight, 10) / 100;
} else {
gl.svgHeight = parseInt(rawHeight, 10);
}
} else {
gl.svgHeight = gl.axisCharts ? gl.svgWidth / 1.61 : gl.svgWidth / 1.2;
}
gl.svgWidth = Math.max(gl.svgWidth, 0);
gl.svgHeight = Math.max(gl.svgHeight, 0);
Graphics.setAttrs(this.w.dom.Paper.node, {
width: gl.svgWidth,
height: gl.svgHeight
});
if (heightUnit !== "%" && Environment.isBrowser()) {
const offsetY = cnf.chart.sparkline.enabled ? 0 : gl.axisCharts ? cnf.chart.parentHeightOffset : 0;
const paperNode = this.w.dom.Paper.node;
if ((_a = paperNode.parentNode) == null ? void 0 : _a.parentNode) {
paperNode.parentNode.parentNode.style.minHeight = `${gl.svgHeight + offsetY}px`;
}
}
this.w.dom.elWrap.style.width = `${gl.svgWidth}px`;
this.w.dom.elWrap.style.height = `${gl.svgHeight}px`;
}
shiftGraphPosition() {
const { globals: gl } = this.w;
const { translateY: tY, translateX: tX } = gl;
Graphics.setAttrs(this.w.dom.elGraphical.node, {
transform: `translate(${tX}, ${tY})`
});
}
resizeNonAxisCharts() {
var _a, _b;
const { w } = this;
let legendHeight = 0;
let offY = w.config.chart.sparkline.enabled ? 1 : 15;
offY += w.config.grid.padding.bottom;
if (["top", "bottom"].includes(w.config.legend.position) && w.config.legend.show && !w.config.legend.floating) {
legendHeight = ((_b = (_a = this.ctx.legend) == null ? void 0 : _a.legendHelpers.getLegendDimensions().clwh) != null ? _b : 0) + 7;
}
const el = w.dom.baseEl.querySelector(
".apexcharts-radialbar, .apexcharts-pie"
);
let chartInnerDimensions = w.globals.radialSize * 2.05;
if (el && !w.config.chart.sparkline.enabled && w.config.plotOptions.radialBar.startAngle !== 0) {
const elRadialRect = Utils$1.getBoundingClientRect(el);
chartInnerDimensions = elRadialRect.bottom;
const maxHeight = elRadialRect.bottom - elRadialRect.top;
chartInnerDimensions = Math.max(w.globals.radialSize * 2.05, maxHeight);
}
const newHeight = Math.ceil(
chartInnerDimensions + this.w.layout.translateY + legendHeight + offY
);
if (this.w.dom.elLegendForeign) {
this.w.dom.elLegendForeign.setAttribute("height", String(newHeight));
}
if (w.config.chart.height && String(w.config.chart.height).includes("%"))
return;
this.w.dom.elWrap.style.height = `${newHeight}px`;
Graphics.setAttrs(this.w.dom.Paper.node, { height: newHeight });
if (Environment.isBrowser()) {
this.w.dom.Paper.node.parentNode.parentNode.style.minHeight = `${newHeight}px`;
}
}
coreCalculations() {
new Range(this.w).init();
}
resetGlobals() {
const resetxyValues = () => this.w.config.series.map(() => []);
const globalObj = new Globals();
const { globals: gl } = this.w;
const parsingFlags = {
dataWasParsed: this.w.axisFlags.dataWasParsed,
originalSeries: gl.originalSeries
};
globalObj.initGlobalVars(gl);
gl.seriesXvalues = resetxyValues();
gl.seriesYvalues = resetxyValues();
if (parsingFlags.dataWasParsed) {
this.w.axisFlags.dataWasParsed = parsingFlags.dataWasParsed;
gl.originalSeries = parsingFlags.originalSeries;
}
}
isMultipleY() {
if (Array.isArray(this.w.config.yaxis) && this.w.config.yaxis.length > 1) {
this.w.globals.isMultipleYAxis = true;
return true;
}
return false;
}
xySettings() {
const { w } = this;
let xyRatios = null;
if (w.globals.axisCharts) {
if (w.config.xaxis.crosshairs.position === "back") {
new Crosshairs(this.w).drawXCrosshairs();
}
if (w.config.yaxis[0].crosshairs.position === "back") {
new Crosshairs(this.w).drawYCrosshairs();
}
if (w.config.xaxis.type === "datetime" && w.config.xaxis.labels.formatter === void 0) {
this.ctx.timeScale = new TimeScale(this.w, this.ctx);
let formattedTimeScale = [];
if (isFinite(w.globals.minX) && isFinite(w.globals.maxX) && !w.globals.isBarHorizontal) {
formattedTimeScale = this.ctx.timeScale.calculateTimeScaleTicks(
w.globals.minX,
w.globals.maxX
);
} else if (w.globals.isBarHorizontal) {
formattedTimeScale = this.ctx.timeScale.calculateTimeScaleTicks(
w.globals.minY,
w.globals.maxY
);
}
this.ctx.timeScale.recalcDimensionsBasedOnFormat(formattedTimeScale);
}
const coreUtils = new CoreUtils(this.w);
xyRatios = coreUtils.getCalculatedRatios();
}
return xyRatios;
}
/**
* @param {any} targetChart
*/
updateSourceChart(targetChart) {
this.ctx.w.interact.selection = void 0;
this.ctx.updateHelpers._updateOptions(
{
chart: {
selection: {
xaxis: {
min: targetChart.w.globals.minX,
max: targetChart.w.globals.maxX
}
}
}
},
false,
false
);
}
setupBrushHandler() {
const { ctx, w } = this;
if (!w.config.chart.brush.enabled) return;
if (typeof w.config.chart.events.selection !== "function") {
const targets = Array.isArray(w.config.chart.brush.targets) ? w.config.chart.brush.targets : [w.config.chart.brush.target];
targets.forEach((target) => {
const targetChart = (
/** @type {any} */
ctx.constructor.getChartByID(
target
)
);
targetChart.w.globals.brushSource = this.ctx;
if (typeof targetChart.w.config.chart.events.zoomed !== "function") {
targetChart.w.config.chart.events.zoomed = () => this.updateSourceChart(targetChart);
}
if (typeof targetChart.w.config.chart.events.scrolled !== "function") {
targetChart.w.config.chart.events.scrolled = () => (
/**
* @param {any} chart
* @param {Event} e
*/
this.updateSourceChart(targetChart)
);
}
});
w.config.chart.events.selection = (chart, e) => {
targets.forEach((target) => {
const targetChart = (
/** @type {any} */
ctx.constructor.getChartByID(
target
)
);
targetChart.ctx.updateHelpers._updateOptions(
{
xaxis: {
min: e.xaxis.min,
max: e.xaxis.max
}
},
false,
false,
false,
false
);
});
};
}
}
getAccessibleChartLabel() {
const w = this.w;
const cnf = w.config;
if (cnf.chart.accessibility && cnf.chart.accessibility.description) {
return cnf.chart.accessibility.description;
}
const chartType = cnf.chart.type;
const parts = [];
if (cnf.title.text) {
parts.push(`${cnf.title.text}. ${chartType} chart`);
if (cnf.subtitle.text) parts.push(cnf.subtitle.text);
} else {
const namedSeries = (() => {
if (Array.isArray(w.seriesData.seriesNames) && w.seriesData.seriesNames.length) {
return w.seriesData.seriesNames.filter(Boolean);
}
if (Array.isArray(cnf.series)) {
return cnf.series.map((s) => typeof s === "object" && s !== null ? s.name : null).filter(Boolean);
}
return [];
})();
const seriesCount = w.seriesData.series.length || (cnf.series ? cnf.series.length : 0);
if (namedSeries.length) {
parts.push(
`${chartType} chart with ${seriesCount} data series: ${namedSeries.join(", ")}`
);
} else {
parts.push(`${chartType} chart with ${seriesCount} data series`);
}
}
return parts.join(". ");
}
}
class Data {
/**
* @param {import('../types/internal').ChartStateW} w
*/
constructor(w, { resetGlobals = () => {
}, isMultipleY = () => {
} } = {}) {
this.w = w;
this.resetGlobals = resetGlobals;
this.isMultipleY = isMultipleY;
this.twoDSeries = [];
this.threeDSeries = [];
this.twoDSeriesX = [];
this.seriesGoals = [];
this.coreUtils = new CoreUtils(this.w);
this.activeSeriesIndex = 0;
}
// Helper to get the first valid data point from the active series
getFirstDataPoint() {
const series = this.w.config.series;
const sr = new Series(this.w);
this.activeSeriesIndex = sr.getActiveConfigSeriesIndex();
const activeItem = (
/** @type {any} */
series[this.activeSeriesIndex]
);
if (activeItem && activeItem.data && activeItem.data.length > 0 && activeItem.data[0] !== null && typeof activeItem.data[0] !== "undefined") {
return activeItem.data[0];
}
return null;
}
isMultiFormat() {
return this.isFormatXY() || this.isFormat2DArray();
}
// given format is [{x, y}, {x, y}]
isFormatXY() {
var _a;
const firstDataPoint = this.getFirstDataPoint();
if (!firstDataPoint || typeof firstDataPoint.x === "undefined") return false;
const data = (
/** @type {any} */
(_a = this.w.config.series[this.activeSeriesIndex]) == null ? void 0 : _a.data
);
if (data) {
const isXY = (pt) => pt && typeof pt.x !== "undefined";
for (let k = 1; k < Math.min(3, data.length); k++) {
if (isXY(data[k]) !== true) {
console.warn(
`ApexCharts: series data has mixed formats starting at index ${k}`
);
break;
}
}
}
return true;
}
// given format is [[x, y], [x, y]]
isFormat2DArray() {
const firstDataPoint = this.getFirstDataPoint();
return firstDataPoint && Array.isArray(firstDataPoint);
}
/**
* @param {any[]} ser
* @param {number} i
*/
handleFormat2DArray(ser, i) {
const cnf = this.w.config;
const data = ser[i].data;
const isBoxPlot = cnf.chart.type === "boxPlot" || /** @type {any} */
cnf.series[i].type === "boxPlot";
for (let j = 0; j < data.length; j++) {
const point = data[j];
const x = point[0];
const y = point[1];
const z = point[2];
if (typeof y !== "undefined") {
if (Array.isArray(y) && y.length === 4 && !isBoxPlot) {
this.twoDSeries.push(Utils$1.parseNumber(y[3]));
} else if (point.length >= 5) {
this.twoDSeries.push(Utils$1.parseNumber(point[4]));
} else {
this.twoDSeries.push(Utils$1.parseNumber(y));
}
this.w.axisFlags.dataFormatXNumeric = true;
}
if (cnf.xaxis.type === "datetime") {
const ts = new Date(x).getTime();
this.twoDSeriesX.push(ts);
} else {
this.twoDSeriesX.push(x);
}
if (typeof z !== "undefined") {
this.threeDSeries.push(z);
this.w.axisFlags.isDataXYZ = true;
}
}
}
/**
* @param {any[]} ser
* @param {number} i
*/
handleFormatXY(ser, i) {
const cnf = this.w.config;
const gl = this.w.globals;
const dt = new DateTime(this.w);
const data = ser[i].data;
let activeI = i;
if (gl.collapsedSeriesIndices.indexOf(i) > -1) {
activeI = this.activeSeriesIndex;
}
const activeData = ser[activeI].data;
for (let j = 0; j < data.length; j++) {
const point = data[j];
if (typeof point.y !== "undefined") {
const val = Array.isArray(point.y) ? Utils$1.parseNumber(point.y[point.y.length - 1]) : Utils$1.parseNumber(point.y);
this.twoDSeries.push(val);
}
if (typeof this.seriesGoals[i] === "undefined") {
this.seriesGoals[i] = [];
}
if (typeof point.goals !== "undefined" && Array.isArray(point.goals)) {
this.seriesGoals[i].push(point.goals);
} else {
this.seriesGoals[i].push(null);
}
if (typeof point.z !== "undefined") {
this.threeDSeries.push(point.z);
this.w.axisFlags.isDataXYZ = true;
}
}
for (let j = 0; j < activeData.length; j++) {
const point = activeData[j];
const x = point.x;
const isXString = typeof x === "string";
const isXArr = Array.isArray(x);
const isXDate = !isXArr && !!dt.isValidDate(x);
if (isXString || isXDate) {
if (isXString || cnf.xaxis.convertedCatToNumeric) {
const isRangeColumn = gl.isBarHorizontal && this.w.axisFlags.isRangeData;
if (cnf.xaxis.type === "datetime" && !isRangeColumn) {
this.twoDSeriesX.push(dt.parseDate(x));
} else {
this.fallbackToCategory = true;
this.twoDSeriesX.push(x);
if (!isNaN(x) && this.w.config.xaxis.type !== "category" && typeof x !== "string") {
this.w.axisFlags.isXNumeric = true;
}
}
} else {
if (cnf.xaxis.type === "datetime") {
this.twoDSeriesX.push(dt.parseDate(x.toString()));
} else {
this.w.axisFlags.dataFormatXNumeric = true;
this.w.axisFlags.isXNumeric = true;
this.twoDSeriesX.push(parseFloat(x));
}
}
} else if (isXArr) {
this.fallbackToCategory = true;
this.twoDSeriesX.push(x);
} else {
this.w.axisFlags.isXNumeric = true;
this.w.axisFlags.dataFormatXNumeric = true;
this.twoDSeriesX.push(x);
}
}
}
/**
* @param {any[]} ser
* @param {number} i
*/
handleRangeData(ser, i) {
let range = { start: [], end: [], rangeUniques: [] };
if (this.isFormat2DArray()) {
range = this.handleRangeDataFormat("array", ser, i);
} else if (this.isFormatXY()) {
range = this.handleRangeDataFormat("xy", ser, i);
}
this.w.rangeData.seriesRangeStart[i] = range.start === void 0 ? [] : range.start;
this.w.rangeData.seriesRangeEnd[i] = range.end === void 0 ? [] : range.end;
this.w.rangeData.seriesRange[i] = range.rangeUniques;
this.w.rangeData.seriesRange.forEach((sr) => {
if (!sr) return;
sr.forEach((sarr) => {
const yItems = (
/** @type {any} */
sarr.y
);
const len = (
/** @type {any[]} */
yItems.length
);
if (len <= 1) return;
for (let arri = 0; arri < len; arri++) {
const arr = (
/** @type {any} */
yItems[arri]
);
const range1y1 = arr.y1;
const range1y2 = arr.y2;
for (let sri = arri + 1; sri < len; sri++) {
const range2 = (
/** @type {any} */
yItems[sri]
);
const range2y1 = range2.y1;
const range2y2 = range2.y2;
if (range1y1 <= range2y2 && range2y1 <= range1y2) {
const sarrAny = (
/** @type {any} */
sarr
);
sarrAny.overlaps.add(arr.rangeName);
sarrAny.overlaps.add(range2.rangeName);
}
}
}
});
});
return range;
}
/**
* @param {any[]} ser
* @param {number} i
*/
handleCandleStickBoxData(ser, i) {
let ohlc = { o: [], h: [], m: [], l: [], c: [] };
if (this.isFormat2DArray()) {
ohlc = this.handleCandleStickBoxDataFormat("array", ser, i);
} else if (this.isFormatXY()) {
ohlc = this.handleCandleStickBoxDataFormat("xy", ser, i);
}
this.w.candleData.seriesCandleO[i] = ohlc.o;
this.w.candleData.seriesCandleH[i] = ohlc.h;
this.w.candleData.seriesCandleM[i] = ohlc.m;
this.w.candleData.seriesCandleL[i] = ohlc.l;
this.w.candleData.seriesCandleC[i] = ohlc.c;
return ohlc;
}
/**
* @param {string} format
* @param {any[]} ser
* @param {number} i
*/
handleRangeDataFormat(format, ser, i) {
const rangeStart = [];
const rangeEnd = [];
const uniqueKeysMap = /* @__PURE__ */ new Map();
const uniqueKeys = [];
ser[i].data.forEach((item) => {
if (!uniqueKeysMap.has(item.x)) {
const keyObj = {
x: item.x,
overlaps: /* @__PURE__ */ new Set(),
y: []
};
uniqueKeysMap.set(item.x, keyObj);
uniqueKeys.push(keyObj);
}
});
if (format === "array") {
for (let j = 0; j < ser[i].data.length; j++) {
if (Array.isArray(ser[i].data[j])) {
rangeStart.push(ser[i].data[j][1][0]);
rangeEnd.push(ser[i].data[j][1][1]);
} else {
rangeStart.push(ser[i].data[j]);
rangeEnd.push(ser[i].data[j]);
}
}
} else if (format === "xy") {
for (let j = 0; j < ser[i].data.length; j++) {
const isDataPoint2D = Array.isArray(ser[i].data[j].y);
const id = Utils$1.randomId();
const x = ser[i].data[j].x;
const y = {
y1: isDataPoint2D ? ser[i].data[j].y[0] : ser[i].data[j].y,
y2: isDataPoint2D ? ser[i].data[j].y[1] : ser[i].data[j].y,
rangeName: id
};
ser[i].data[j].rangeName = id;
const keyObj = uniqueKeysMap.get(x);
if (keyObj) {
keyObj.y.push(y);
}
rangeStart.push(y.y1);
rangeEnd.push(y.y2);
}
}
return {
start: rangeStart,
end: rangeEnd,
rangeUniques: uniqueKeys
};
}
/**
* @param {string} format
* @param {any[]} ser
* @param {number} i
*/
handleCandleStickBoxDataFormat(format, ser, i) {
const w = this.w;
const isBoxPlot = w.config.chart.type === "boxPlot" || /** @type {Record<string,any>} */
w.config.series[i].type === "boxPlot";
const serO = [];
const serH = [];
const serM = [];
const serL = [];
const serC = [];
const data = ser[i].data;
let getVals;
if (format === "array") {
const isFlat = isBoxPlot && data[0].length === 6 || !isBoxPlot && data[0].length === 5;
if (isFlat) {
getVals = (d) => d.slice(1);
} else {
getVals = (d) => Array.isArray(d[1]) ? d[1] : [];
}
} else {
getVals = (d) => Array.isArray(d.y) ? d.y : [];
}
for (let j = 0; j < data.length; j++) {
const vals = getVals(data[j]);
if (vals && vals.length >= 2) {
serO.push(vals[0]);
serH.push(vals[1]);
if (isBoxPlot) {
serM.push(vals[2]);
serL.push(vals[3]);
serC.push(vals[4]);
} else {
serL.push(vals[2]);
serC.push(vals[3]);
}
}
}
return {
o: serO,
h: serH,
m: serM,
l: serL,
c: serC
};
}
/**
* @param {any[]} ser
*/
parseDataAxisCharts(ser) {
var _a, _b;
const cnf = this.w.config;
const gl = this.w.globals;
const dt = new DateTime(this.w);
const xlabels = cnf.labels.length > 0 ? cnf.labels.slice() : cnf.xaxis.categories.slice();
this.w.axisFlags.isRangeBar = cnf.chart.type === "rangeBar" && gl.isBarHorizontal;
this.w.labelData.hasXaxisGroups = cnf.xaxis.type === "category" && cnf.xaxis.group.groups.length > 0;
if (this.w.labelData.hasXaxisGroups) {
this.w.labelData.groups = cnf.xaxis.group.groups;
}
ser.forEach((s, i) => {
if (s.name !== void 0) {
this.w.seriesData.seriesNames.push(s.name);
} else {
this.w.seriesData.seriesNames.push(
"series-" + parseInt(String(i + 1), 10)
);
}
});
this.coreUtils.setSeriesYAxisMappings();
const buckets = [];
const groups = [
...new Set(cnf.series.map((s) => s.group))
];
cnf.series.forEach((s, i) => {
const index = groups.indexOf(s.group);
if (!buckets[index]) buckets[index] = [];
buckets[index].push(this.w.seriesData.seriesNames[i]);
});
this.w.labelData.seriesGroups = buckets;
const handleDates = () => {
for (let j = 0; j < xlabels.length; j++) {
if (typeof xlabels[j] === "string") {
const isDate = dt.isValidDate(xlabels[j]);
if (isDate) {
this.twoDSeriesX.push(dt.parseDate(xlabels[j]));
} else {
throw new Error(
"You have provided invalid Date format. Please provide a valid JavaScript Date"
);
}
} else {
this.twoDSeriesX.push(xlabels[j]);
}
}
};
for (let i = 0; i < ser.length; i++) {
this.twoDSeries = [];
this.twoDSeriesX = [];
this.threeDSeries = [];
if (typeof ser[i].data === "undefined") {
console.error(
"It is a possibility that you may have not included 'data' property in series."
);
return;
}
const dr = cnf.chart.dataReducer;
if ((dr == null ? void 0 : dr.enabled) && this.isMultiFormat() && ser[i].data.length > ((_a = dr.threshold) != null ? _a : 500)) {
ser[i] = __spreadProps(__spreadValues({}, ser[i]), {
data: Data.lttbDownsample(ser[i].data, (_b = dr.targetPoints) != null ? _b : 250)
});
}
if (cnf.chart.type === "rangeBar" || cnf.chart.type === "rangeArea" || ser[i].type === "rangeBar" || ser[i].type === "rangeArea") {
this.w.axisFlags.isRangeData = true;
this.handleRangeData(ser, i);
}
if (this.isMultiFormat()) {
if (this.isFormat2DArray()) {
this.handleFormat2DArray(ser, i);
} else if (this.isFormatXY()) {
this.handleFormatXY(ser, i);
}
if (cnf.chart.type === "candlestick" || ser[i].type === "candlestick" || cnf.chart.type === "boxPlot" || ser[i].type === "boxPlot") {
this.handleCandleStickBoxData(ser, i);
}
this.w.seriesData.series.push(this.twoDSeries);
this.w.labelData.labels.push(this.twoDSeriesX);
this.w.seriesData.seriesX.push(this.twoDSeriesX);
this.w.seriesData.seriesGoals = this.seriesGoals;
if (i === this.activeSeriesIndex && !this.fallbackToCategory) {
this.w.axisFlags.isXNumeric = true;
}
} else {
if (cnf.xaxis.type === "datetime") {
this.w.axisFlags.isXNumeric = true;
handleDates();
this.w.seriesData.seriesX.push(this.twoDSeriesX);
} else if (cnf.xaxis.type === "numeric") {
this.w.axisFlags.isXNumeric = true;
if (xlabels.length > 0) {
this.twoDSeriesX = xlabels;
this.w.seriesData.seriesX.push(this.twoDSeriesX);
}
}
this.w.labelData.labels.push(this.twoDSeriesX);
const singleArray = ser[i].data.map(
(d) => Utils$1.parseNumber(d)
);
this.w.seriesData.series.push(singleArray);
}
this.w.seriesData.seriesZ.push(this.threeDSeries);
if (ser[i].color !== void 0) {
this.w.seriesData.seriesColors.push(ser[i].color);
} else {
this.w.seriesData.seriesColors.push(
/** @type {any} */
void 0
);
}
}
return this.w;
}
/**
* @param {any[]} ser
*/
parseDataNonAxisCharts(ser) {
const cnf = this.w.config;
const hasOldFormat = Array.isArray(ser) && ser.every((s) => typeof s === "number") && cnf.labels.length > 0;
const hasNewFormat = Array.isArray(ser) && ser.some(
(s) => s && typeof s === "object" && s.data || s && typeof s === "object" && s.parsing
);
if (hasOldFormat && hasNewFormat) {
console.warn(
"ApexCharts: Both old format (numeric series + labels) and new format (series objects with data/parsing) detected. Using old format for backward compatibility."
);
}
if (hasOldFormat) {
this.w.seriesData.series = /** @type {any} */
ser.slice();
this.w.seriesData.seriesNames = cnf.labels.slice();
for (let i = 0; i < this.w.seriesData.series.length; i++) {
if (this.w.seriesData.seriesNames[i] === void 0) {
this.w.seriesData.seriesNames.push("series-" + (i + 1));
}
}
return this.w;
}
if (Array.isArray(ser) && ser.every((s) => typeof s === "number")) {
this.w.seriesData.series = /** @type {any} */
ser.slice();
this.w.seriesData.seriesNames = [];
for (let i = 0; i < this.w.seriesData.series.length; i++) {
this.w.seriesData.seriesNames.push(cnf.labels[i] || `series-${i + 1}`);
}
return this.w;
}
const processedData = this.extractPieDataFromSeries(ser);
this.w.seriesData.series = processedData.values;
this.w.seriesData.seriesNames = processedData.labels;
if (cnf.chart.type === "radialBar") {
this.w.seriesData.series = this.w.seriesData.series.map((val) => {
const numVal = Utils$1.parseNumber(val);
if (numVal > 100) {
console.warn(
`ApexCharts: RadialBar value ${numVal} > 100, consider using percentage values (0-100)`
);
}
return numVal;
});
}
for (let i = 0; i < this.w.seriesData.series.length; i++) {
if (this.w.seriesData.seriesNames[i] === void 0) {
this.w.seriesData.seriesNames.push("series-" + (i + 1));
}
}
return this.w;
}
/**
* Reset parsing flags to allow re-parsing of data during updates
*/
resetParsingFlags() {
const w = this.w;
w.axisFlags.dataWasParsed = false;
w.globals.originalSeries = null;
if (w.config.series) {
w.config.series.forEach((serie) => {
if (
/** @type {any} */
serie.__apexParsed
) {
delete /** @type {any} */
serie.__apexParsed;
}
});
}
}
/**
* @param {any[]} ser
*/
extractPieDataFromSeries(ser) {
const values = [];
const labels = [];
if (!Array.isArray(ser)) {
console.warn("ApexCharts: Expected array for series data");
return { values: [], labels: [] };
}
if (ser.length === 0) {
console.warn("ApexCharts: Empty series array");
return { values: [], labels: [] };
}
const firstItem = ser[0];
if (typeof firstItem === "object" && firstItem !== null && firstItem.data) {
this.extractPieDataFromSeriesObjects(ser, values, labels);
} else {
console.warn(
"ApexCharts: Unsupported series format for pie/donut/radialBar. Expected series objects with data property."
);
return { values: [], labels: [] };
}
return { values, labels };
}
// Extract data from series objects: [{ data: [...], parsing: {...} }]
/**
* @param {any[]} seriesArray
* @param {any[]} values
* @param {any[]} labels
*/
extractPieDataFromSeriesObjects(seriesArray, values, labels) {
seriesArray.forEach((serie, serieIndex) => {
if (!serie.data || !Array.isArray(serie.data)) {
console.warn(`ApexCharts: Series ${serieIndex} has no valid data array`);
return;
}
serie.data.forEach((dataPoint) => {
if (typeof dataPoint === "object" && dataPoint !== null) {
if (dataPoint.x !== void 0 && dataPoint.y !== void 0) {
labels.push(String(dataPoint.x));
values.push(Utils$1.parseNumber(dataPoint.y));
} else {
console.warn(
"ApexCharts: Invalid data point format for pie chart. Expected {x, y} format:",
dataPoint
);
}
} else {
console.warn(
"ApexCharts: Expected object data point, got:",
typeof dataPoint
);
}
});
});
}
/** User possibly set string categories in xaxis.categories or labels prop
* Or didn't set xaxis labels at all - in which case we manually do it.
* If user passed series data as [[3, 2], [4, 5]] or [{ x: 3, y: 55 }],
* this shouldn't be called
* @param {any[]} ser - the series which user passed to the config
*/
handleExternalLabelsData(ser) {
const cnf = this.w.config;
if (cnf.xaxis.categories.length > 0) {
this.w.labelData.labels = cnf.xaxis.categories;
} else if (cnf.labels.length > 0) {
this.w.labelData.labels = cnf.labels.slice();
} else if (this.fallbackToCategory) {
this.w.labelData.labels = /** @type {string[]} */
/** @type {unknown} */
this.w.labelData.labels[0];
if (this.w.rangeData.seriesRange.length) {
this.w.rangeData.seriesRange.map((srt) => {
srt.forEach((sr) => {
if (this.w.labelData.labels.indexOf(sr.x) < 0 && sr.x) {
this.w.labelData.labels.push(sr.x);
}
});
});
const _labels = this.w.labelData.labels;
if (_labels.length > 0 && (typeof _labels[0] === "number" || typeof _labels[0] === "string")) {
this.w.labelData.labels = [...new Set(_labels)];
} else {
const _seen = /* @__PURE__ */ new Map();
for (const _label of _labels) {
const _key = JSON.stringify(_label);
if (!_seen.has(_key)) _seen.set(_key, _label);
}
this.w.labelData.labels = Array.from(_seen.values());
}
}
if (cnf.xaxis.convertedCatToNumeric) {
const defaults = new Defaults(cnf);
defaults.convertCatToNumericXaxis(cnf, this.w.seriesData.seriesX[0]);
this._generateExternalLabels(ser);
}
} else {
this._generateExternalLabels(ser);
}
}
/**
* @param {any[]} ser
*/
_generateExternalLabels(ser) {
const gl = this.w.globals;
const cnf = this.w.config;
let labelArr = [];
if (gl.axisCharts) {
if (this.w.seriesData.series.length > 0) {
if (this.isFormatXY()) {
const seriesDataFiltered = cnf.series.map(
(serie) => {
const seen = /* @__PURE__ */ new Map();
for (const point of serie.data) {
if (!seen.has(point.x)) seen.set(point.x, point);
}
return Array.from(seen.values());
}
);
const len = seriesDataFiltered.reduce(
(p, c, i, a) => a[p].length > c.length ? p : i,
0
);
for (let i = 0; i < seriesDataFiltered[len].length; i++) {
labelArr.push(i + 1);
}
} else {
for (let i = 0; i < this.w.seriesData.series[gl.maxValsInArrayIndex].length; i++) {
labelArr.push(i + 1);
}
}
}
this.w.seriesData.seriesX = [];
for (let i = 0; i < ser.length; i++) {
this.w.seriesData.seriesX.push(labelArr);
}
if (!this.w.globals.isBarHorizontal) {
this.w.axisFlags.isXNumeric = true;
}
}
if (labelArr.length === 0) {
labelArr = gl.axisCharts ? [] : (
/**
* @param {Record<string, any>} gls
* @param {number} glsi
*/
this.w.seriesData.series.map((gls, glsi) => {
return glsi + 1;
})
);
for (let i = 0; i < ser.length; i++) {
this.w.seriesData.seriesX.push(labelArr);
}
}
this.w.labelData.labels = /** @type {string[]} */
/** @type {unknown} */
labelArr;
if (cnf.xaxis.convertedCatToNumeric) {
this.w.labelData.categoryLabels = labelArr.map((l) => {
return cnf.xaxis.labels.formatter(l);
});
}
this.w.axisFlags.noLabelsProvided = true;
}
/**
* @param {any[]} series
*/
parseRawDataIfNeeded(series) {
const cnf = this.w.config;
const gl = this.w.globals;
const globalParsing = cnf.parsing;
if (this.w.axisFlags.dataWasParsed) {
return series;
}
if (!globalParsing && !series.some((s) => s.parsing)) {
return series;
}
const processedSeries = series.map((serie, index) => {
var _a, _b, _c;
if (!serie.data || !Array.isArray(serie.data) || serie.data.length === 0) {
return serie;
}
const effectiveParsing = {
x: ((_a = serie.parsing) == null ? void 0 : _a.x) || (globalParsing == null ? void 0 : globalParsing.x),
y: ((_b = serie.parsing) == null ? void 0 : _b.y) || (globalParsing == null ? void 0 : globalParsing.y),
z: ((_c = serie.parsing) == null ? void 0 : _c.z) || (globalParsing == null ? void 0 : globalParsing.z)
};
if (!effectiveParsing.x && !effectiveParsing.y) {
return serie;
}
const firstDataPoint = serie.data[0];
if (typeof firstDataPoint === "object" && firstDataPoint !== null && (Object.prototype.hasOwnProperty.call(firstDataPoint, "x") || Object.prototype.hasOwnProperty.call(firstDataPoint, "y")) || Array.isArray(firstDataPoint)) {
return serie;
}
if (!effectiveParsing.x || !effectiveParsing.y || Array.isArray(effectiveParsing.y) && effectiveParsing.y.length === 0) {
console.warn(
`ApexCharts: Series ${index} has parsing config but missing x or y field specification`
);
return serie;
}
const transformedData = serie.data.map(
(item, itemIndex) => {
if (typeof item !== "object" || item === null) {
console.warn(
`ApexCharts: Series ${index}, data point ${itemIndex} is not an object, skipping parsing`
);
return item;
}
const x = this.getNestedValue(item, effectiveParsing.x);
let y;
let z = void 0;
if (Array.isArray(effectiveParsing.y)) {
const yValues = effectiveParsing.y.map(
(fieldName) => this.getNestedValue(item, fieldName)
);
if (this.w.config.chart.type === "bubble") {
if (yValues.length < 2) {
console.warn(
`ApexCharts: series[${index}] bubble chart requires parseData.y to have at least 2 fields (y and z). Got: ${JSON.stringify(effectiveParsing.y)}`
);
}
y = yValues[0];
} else {
y = yValues;
}
} else {
y = this.getNestedValue(item, effectiveParsing.y);
}
if (effectiveParsing.z) {
z = this.getNestedValue(item, effectiveParsing.z);
}
if (x === void 0) {
console.warn(
`ApexCharts: Series ${index}, data point ${itemIndex} missing field '${effectiveParsing.x}'`
);
}
if (y === void 0) {
console.warn(
`ApexCharts: Series ${index}, data point ${itemIndex} missing field '${effectiveParsing.y}'`
);
}
const result = { x, y, z: void 0 };
if (this.w.config.chart.type === "bubble" && Array.isArray(effectiveParsing.y) && effectiveParsing.y.length === 2) {
const zValue = this.getNestedValue(item, effectiveParsing.y[1]);
if (zValue !== void 0) {
result.z = zValue;
}
}
if (z !== void 0) {
result.z = z;
}
return result;
}
);
return __spreadProps(__spreadValues({}, serie), {
data: transformedData,
__apexParsed: true
});
});
this.w.axisFlags.dataWasParsed = true;
if (!gl.originalSeries) {
gl.originalSeries = Utils$1.clone(series);
}
return processedSeries;
}
/**
* Get nested object value using dot notation path
* @param {Object} obj - The object to search in
* @param {string} path - Dot notation path (e.g., 'user.profile.name')
* @returns {*} The value at the path, or undefined if not found
*/
getNestedValue(obj, path) {
if (!obj || typeof obj !== "object" || !path) {
return void 0;
}
if (path.indexOf(".") === -1) {
return (
/** @type {any} */
obj[path]
);
}
const keys = path.split(".");
let current = obj;
for (let i = 0; i < keys.length; i++) {
if (current === null || current === void 0 || typeof current !== "object") {
return void 0;
}
current = /** @type {any} */
current[keys[i]];
}
return current;
}
// Segregate user provided data into appropriate vars
/**
* @param {any[]} ser
*/
parseData(ser) {
const w = this.w;
const cnf = w.config;
const gl = w.globals;
ser = this.parseRawDataIfNeeded(ser);
cnf.series = ser;
gl.initialSeries = Utils$1.clone(ser);
this.excludeCollapsedSeriesInYAxis();
this.fallbackToCategory = false;
this.resetGlobals();
this.isMultipleY();
if (gl.axisCharts) {
this.parseDataAxisCharts(ser);
this.coreUtils.getLargestSeries();
} else {
this.parseDataNonAxisCharts(ser);
}
if (cnf.chart.stacked) {
const series = new Series(this.w);
this.w.seriesData.series = series.setNullSeriesToZeroValues(
this.w.seriesData.series
);
}
this.coreUtils.getSeriesTotals();
if (gl.axisCharts) {
this.w.seriesData.stackedSeriesTotals = this.coreUtils.getStackedSeriesTotals();
this.w.seriesData.stackedSeriesTotalsByGroups = this.coreUtils.getStackedSeriesTotalsByGroups();
}
this.coreUtils.getPercentSeries();
if (!this.w.axisFlags.dataFormatXNumeric && (!this.w.axisFlags.isXNumeric || cnf.xaxis.type === "numeric" && cnf.labels.length === 0 && cnf.xaxis.categories.length === 0)) {
this.handleExternalLabelsData(ser);
}
const catLabels = this.coreUtils.getCategoryLabels(this.w.labelData.labels);
for (let l = 0; l < catLabels.length; l++) {
if (Array.isArray(catLabels[l])) {
this.w.axisFlags.isMultiLineX = true;
break;
}
}
return {
// w.seriesData (future slice)
seriesData: {
series: this.w.seriesData.series,
seriesNames: this.w.seriesData.seriesNames,
seriesX: this.w.seriesData.seriesX,
seriesZ: this.w.seriesData.seriesZ,
seriesColors: this.w.seriesData.seriesColors,
seriesGoals: this.w.seriesData.seriesGoals,
initialSeries: gl.initialSeries,
originalSeries: gl.originalSeries,
stackedSeriesTotals: this.w.seriesData.stackedSeriesTotals,
stackedSeriesTotalsByGroups: this.w.seriesData.stackedSeriesTotalsByGroups,
noLabelsProvided: this.w.axisFlags.noLabelsProvided
},
// w.rangeData (future slice)
rangeData: {
seriesRangeStart: this.w.rangeData.seriesRangeStart,
seriesRangeEnd: this.w.rangeData.seriesRangeEnd,
seriesRange: this.w.rangeData.seriesRange
},
// w.candleData (future slice)
candleData: {
seriesCandleO: this.w.candleData.seriesCandleO,
seriesCandleH: this.w.candleData.seriesCandleH,
seriesCandleM: this.w.candleData.seriesCandleM,
seriesCandleL: this.w.candleData.seriesCandleL,
seriesCandleC: this.w.candleData.seriesCandleC
},
// w.labelData (future slice)
labelData: {
labels: this.w.labelData.labels,
categoryLabels: this.w.labelData.categoryLabels
},
// w.axisFlags (future slice)
axisFlags: {
isXNumeric: this.w.axisFlags.isXNumeric,
dataFormatXNumeric: this.w.axisFlags.dataFormatXNumeric,
isDataXYZ: this.w.axisFlags.isDataXYZ,
isRangeData: this.w.axisFlags.isRangeData,
isRangeBar: this.w.axisFlags.isRangeBar,
isMultiLineX: this.w.axisFlags.isMultiLineX,
dataWasParsed: this.w.axisFlags.dataWasParsed,
hasXaxisGroups: this.w.labelData.hasXaxisGroups,
groups: this.w.labelData.groups,
seriesGroups: this.w.labelData.seriesGroups
}
};
}
/**
* Largest-Triangle-Three-Bucket (LTTB) downsampling.
*
* Reduces `data` to `targetPoints` points while preserving the visual shape
* of the series as perceived by the human eye.
*
* @param {any[]} data - Raw series data in [{x,y}] or [[x,y]] format.
* @param {number} targetPoints - Desired output length (>= 3).
* @returns {any[]} Downsampled array in the same format as the input.
*/
static lttbDownsample(data, targetPoints) {
const len = data.length;
if (targetPoints >= len || targetPoints < 3) return data;
const isXY = !Array.isArray(data[0]);
const getX = isXY ? (p) => p.x : (p) => p[0];
const getY = isXY ? (p) => p.y : (p) => p[1];
const sampled = [];
sampled.push(data[0]);
const bucketSize = (len - 2) / (targetPoints - 2);
let a = 0;
for (let i = 0; i < targetPoints - 2; i++) {
const avgRangeStart = Math.floor((i + 1) * bucketSize) + 1;
const avgRangeEnd = Math.min(Math.floor((i + 2) * bucketSize) + 1, len);
let avgX = 0;
let avgY = 0;
const avgRangeLen = avgRangeEnd - avgRangeStart;
for (let j = avgRangeStart; j < avgRangeEnd; j++) {
avgX += getX(data[j]);
avgY += getY(data[j]);
}
avgX /= avgRangeLen;
avgY /= avgRangeLen;
const rangeStart = Math.floor(i * bucketSize) + 1;
const rangeEnd = Math.min(Math.floor((i + 1) * bucketSize) + 1, len);
const pointAX = getX(data[a]);
const pointAY = getY(data[a]);
let maxArea = -1;
let maxAreaIdx = rangeStart;
for (let j = rangeStart; j < rangeEnd; j++) {
const area = Math.abs(
(pointAX - avgX) * (getY(data[j]) - pointAY) - (pointAX - getX(data[j])) * (avgY - pointAY)
) * 0.5;
if (area > maxArea) {
maxArea = area;
maxAreaIdx = j;
}
}
sampled.push(data[maxAreaIdx]);
a = maxAreaIdx;
}
sampled.push(data[len - 1]);
return sampled;
}
excludeCollapsedSeriesInYAxis() {
const w = this.w;
const yAxisIndexes = [];
w.globals.seriesYAxisMap.forEach((yAxisArr, yi) => {
let collapsedCount = 0;
yAxisArr.forEach((seriesIndex) => {
if (w.globals.collapsedSeriesIndices.indexOf(seriesIndex) !== -1) {
collapsedCount++;
}
});
if (collapsedCount > 0 && collapsedCount == yAxisArr.length) {
yAxisIndexes.push(yi);
}
});
w.globals.ignoreYAxisIndexes = yAxisIndexes.map((x) => x);
}
}
class UpdateHelpers {
/**
* @param {import('../../types/internal').ChartStateW} w
* @param {import('../../types/internal').ChartContext} ctx
*/
constructor(w, ctx) {
this.w = w;
this.ctx = ctx;
}
/**
* private method to update Options.
*
* @param {Record<string, any>} options - A new config object can be passed which will be merged with the existing config object
* @param {boolean} redraw - should redraw from beginning or should use existing paths and redraw from there
* @param {boolean} animate - should animate or not on updating Options
* @param {boolean} overwriteInitialConfig - should update the initial config or not
*/
_updateOptions(options2, redraw = false, animate = true, updateSyncedCharts = true, overwriteInitialConfig = false) {
return new Promise((resolve) => {
let charts = [this.ctx];
if (updateSyncedCharts) {
charts = this.ctx.getSyncedCharts();
}
if (this.w.globals.isExecCalled) {
charts = [this.ctx];
this.w.globals.isExecCalled = false;
}
charts.forEach((ch, chartIndex) => {
const w = ch.w;
w.globals.shouldAnimate = animate;
if (!redraw) {
w.globals.resized = true;
w.globals.dataChanged = true;
if (animate) {
ch.series.getPreviousPaths();
}
}
if (options2 && typeof options2 === "object") {
ch.config = new Config(options2);
options2 = CoreUtils.extendArrayProps(ch.config, options2, w);
if (ch.w.globals.chartID !== this.w.globals.chartID) {
delete options2.series;
}
w.config = Utils$1.extend(w.config, options2);
if (overwriteInitialConfig) {
w.globals.lastXAxis = options2.xaxis ? Utils$1.clone(options2.xaxis) : [];
w.globals.lastYAxis = options2.yaxis ? Utils$1.clone(options2.yaxis) : [];
w.globals.initialConfig = Utils$1.extend({}, w.config);
w.globals.initialSeries = Utils$1.clone(w.config.series);
if (options2.series) {
for (let i = 0; i < w.globals.collapsedSeriesIndices.length; i++) {
const series = w.config.series[w.globals.collapsedSeriesIndices[i]];
w.globals.collapsedSeries[i].data = w.globals.axisCharts ? (
/** @type {any} */
series.data.slice()
) : series;
}
for (let i = 0; i < w.globals.ancillaryCollapsedSeriesIndices.length; i++) {
const series = w.config.series[w.globals.ancillaryCollapsedSeriesIndices[i]];
w.globals.ancillaryCollapsedSeries[i].data = w.globals.axisCharts ? (
/** @type {any} */
series.data.slice()
) : series;
}
ch.series.emptyCollapsedSeries(w.config.series);
}
}
}
return ch.update(options2).then(() => {
if (chartIndex === charts.length - 1) {
resolve(ch);
}
});
});
});
}
/**
* Private method to update Series.
*
* @param {any[]} newSeries - New series which will override the existing
* @param {boolean} animate
*/
_updateSeries(newSeries, animate, overwriteInitialSeries = false) {
return new Promise((resolve) => {
const w = this.w;
w.globals.shouldAnimate = animate;
w.globals.dataChanged = true;
PerformanceCache.invalidateSelectors(w);
if (animate) {
this.ctx.series.getPreviousPaths();
}
const prevSeriesCount = w.config.series.length;
this.ctx.data.resetParsingFlags();
const parsedState = this.ctx.data.parseData(newSeries);
this.ctx._writeParsedSeriesData(parsedState.seriesData);
this.ctx._writeParsedRangeData(parsedState.rangeData);
this.ctx._writeParsedCandleData(parsedState.candleData);
this.ctx._writeParsedLabelData(parsedState.labelData);
this.ctx._writeParsedAxisFlags(parsedState.axisFlags);
if (overwriteInitialSeries) {
if (w.globals.initialConfig) {
w.globals.initialConfig.series = Utils$1.clone(w.config.series);
}
w.globals.initialSeries = Utils$1.clone(w.config.series);
}
if (this._canUseFastPath(newSeries, prevSeriesCount, w)) {
return this.ctx.fastUpdate(animate).then(() => {
resolve(this.ctx);
});
}
return this.ctx.update().then(() => {
resolve(this.ctx);
});
});
}
/**
* Returns true if the data-only fast path can be used for this update.
* Fast path skips rebuilding grid, axes, legend, annotations, and tooltip DOM.
*
* Requirements:
* - Chart has been fully rendered (DOM exists)
* - Axis chart (non-axis charts like pie always need full rebuild due to radial layout)
* - Series count unchanged (grid column/row counts depend on it)
* - No series currently collapsing (collapsed series changes visible data range)
* - Not a combo chart (combo charts mix types and need coordinated axis recalc)
* - Not currently zoomed (zoomed charts have altered x-labels that need recalculation)
* @param {any[]} newSeries
* @param {number} prevSeriesCount
* @param {import('../../types/internal').ChartStateW} w
*/
_canUseFastPath(newSeries, prevSeriesCount, w) {
if (!w.dom.elGraphical) return false;
if (!w.globals.axisCharts) return false;
if (newSeries.length !== prevSeriesCount) return false;
if (w.globals.collapsedSeries.length > 0) return false;
if (w.globals.ancillaryCollapsedSeries.length > 0) return false;
if (w.globals.risingSeries.length > 0) return false;
if (w.globals.comboCharts) return false;
if (w.interact.zoomed) return false;
return true;
}
/**
* @param {any} s
* @param {number} i
*/
_extendSeries(s, i) {
const w = this.w;
const ser = w.config.series[i];
return __spreadProps(__spreadValues(
{},
/** @type {Record<string,any>} */
w.config.series[i]
), {
name: s.name ? s.name : (
/** @type {any} */
ser == null ? void 0 : ser.name
),
color: s.color ? s.color : (
/** @type {any} */
ser == null ? void 0 : ser.color
),
type: s.type ? s.type : (
/** @type {any} */
ser == null ? void 0 : ser.type
),
group: s.group ? s.group : (
/** @type {any} */
ser == null ? void 0 : ser.group
),
hidden: typeof s.hidden !== "undefined" ? s.hidden : (
/** @type {any} */
ser == null ? void 0 : ser.hidden
),
data: s.data ? s.data : (
/** @type {any} */
ser == null ? void 0 : ser.data
),
zIndex: typeof s.zIndex !== "undefined" ? s.zIndex : i
});
}
/**
* @param {number} seriesIndex
* @param {number} dataPointIndex
*/
toggleDataPointSelection(seriesIndex, dataPointIndex) {
const w = this.w;
let elPath = null;
const parent = `.apexcharts-series[data\\:realIndex='${seriesIndex}']`;
if (w.globals.axisCharts) {
elPath = w.dom.Paper.findOne(
`${parent} path[j='${dataPointIndex}'], ${parent} circle[j='${dataPointIndex}'], ${parent} rect[j='${dataPointIndex}']`
);
} else {
if (typeof dataPointIndex === "undefined") {
elPath = w.dom.Paper.findOne(`${parent} path[j='${seriesIndex}']`);
if (w.config.chart.type === "pie" || w.config.chart.type === "polarArea" || w.config.chart.type === "donut") {
this.ctx.pie.pieClicked(seriesIndex);
}
}
}
if (elPath) {
const graphics = new Graphics(this.w);
graphics.pathMouseDown(
elPath,
/** @type {any} */
null
);
} else {
console.warn("toggleDataPointSelection: Element not found");
return null;
}
return elPath.node ? elPath.node : null;
}
/**
* @param {Record<string, any>} options
*/
forceXAxisUpdate(options2) {
const w = this.w;
const minmax = ["min", "max"];
minmax.forEach((a) => {
if (typeof options2.xaxis[a] !== "undefined") {
w.config.xaxis[a] = options2.xaxis[a];
w.globals.lastXAxis[a] = options2.xaxis[a];
}
});
if (options2.xaxis.categories && options2.xaxis.categories.length) {
w.config.xaxis.categories = options2.xaxis.categories;
}
if (w.config.xaxis.convertedCatToNumeric) {
const defaults = new Defaults(options2);
options2 = defaults.convertCatToNumericXaxis(options2, this.ctx);
}
return options2;
}
/**
* @param {Record<string, any>} options
*/
forceYAxisUpdate(options2) {
if (options2.chart && options2.chart.stacked && options2.chart.stackType === "100%") {
if (Array.isArray(options2.yaxis)) {
options2.yaxis.forEach(
(yaxe, index) => {
options2.yaxis[index].min = 0;
options2.yaxis[index].max = 100;
}
);
} else {
options2.yaxis.min = 0;
options2.yaxis.max = 100;
}
}
return options2;
}
/**
* This function reverts the yaxis and xaxis min/max values to what it was when the chart was defined.
* This function fixes an important bug where a user might load a new series after zooming in/out of previous series which resulted in wrong min/max
* Also, this should never be called internally on zoom/pan - the reset should only happen when user calls the updateSeries() function externally
* The function also accepts an object {xaxis, yaxis} which when present is set as the new xaxis/yaxis
* @param {Record<string, any>} opts
*/
revertDefaultAxisMinMax(opts) {
const w = this.w;
let xaxis = w.globals.lastXAxis;
let yaxis = w.globals.lastYAxis;
if (opts && opts.xaxis) {
xaxis = opts.xaxis;
}
if (opts && opts.yaxis) {
yaxis = opts.yaxis;
}
const _xaxis = (
/** @type {any} */
xaxis
);
w.config.xaxis.min = _xaxis.min;
w.config.xaxis.max = _xaxis.max;
const getLastYAxis = (index) => {
if (typeof yaxis[index] !== "undefined") {
const _y = (
/** @type {any} */
yaxis[index]
);
w.config.yaxis[index].min = _y.min;
w.config.yaxis[index].max = _y.max;
}
};
w.config.yaxis.map((yaxe, index) => {
if (w.interact.zoomed) {
getLastYAxis(index);
} else {
if (typeof yaxis[index] !== "undefined") {
getLastYAxis(index);
} else {
if (typeof this.ctx.opts.yaxis[index] !== "undefined") {
yaxe.min = this.ctx.opts.yaxis[index].min;
yaxe.max = this.ctx.opts.yaxis[index].max;
}
}
}
});
}
}
class Utils2 {
/**
* @param {import('./Tooltip').default} tooltipContext
*/
constructor(tooltipContext) {
this.w = tooltipContext.w;
this.ttCtx = tooltipContext;
}
/**
** When hovering over series, you need to capture which series is being hovered on.
** This function will return both capturedseries index as well as inner index of that series
* @memberof Utils
* @param {{ hoverArea: any, elGrid: any, clientX: any, clientY: any, context?: any }} opts
*/
getNearestValues({ hoverArea, elGrid, clientX, clientY }) {
var _a, _b;
const w = this.w;
const seriesBound = elGrid.getBoundingClientRect();
const hoverWidth = seriesBound.width;
const hoverHeight = seriesBound.height;
let xDivisor = hoverWidth / (w.globals.dataPoints - 1);
const yDivisor = hoverHeight / w.globals.dataPoints;
const hasBars = this.hasBars();
if ((w.globals.comboCharts || hasBars) && !w.config.xaxis.convertedCatToNumeric) {
xDivisor = hoverWidth / w.globals.dataPoints;
}
const hoverX = clientX - seriesBound.left - w.globals.barPadForNumericAxis;
const hoverY = clientY - seriesBound.top;
const notInRect = hoverX < 0 || hoverY < 0 || hoverX > hoverWidth || hoverY > hoverHeight;
if (notInRect) {
hoverArea.classList.remove("hovering-zoom");
hoverArea.classList.remove("hovering-pan");
} else {
if (w.interact.zoomEnabled) {
hoverArea.classList.remove("hovering-pan");
hoverArea.classList.add("hovering-zoom");
} else if (w.interact.panEnabled) {
hoverArea.classList.remove("hovering-zoom");
hoverArea.classList.add("hovering-pan");
}
}
let j = Math.round(hoverX / xDivisor);
const jHorz = Math.floor(hoverY / yDivisor);
if (hasBars && !w.config.xaxis.convertedCatToNumeric) {
j = Math.ceil(hoverX / xDivisor);
j = j - 1;
}
let capturedSeries = null;
let closest = null;
let seriesXValArr = w.globals.seriesXvalues.map(
(seriesXVal) => {
return seriesXVal.filter(
(s) => Utils$1.isNumber(s)
);
}
);
const seriesYValArr = w.globals.seriesYvalues.map(
(seriesYVal) => {
return seriesYVal.filter(
(s) => Utils$1.isNumber(s)
);
}
);
if (w.axisFlags.isXNumeric) {
const chartGridEl = this.ttCtx.getElGrid();
if (!chartGridEl) return { hoverX, hoverY };
const chartGridElBoundingRect = chartGridEl.getBoundingClientRect();
const transformedHoverX = hoverX * (chartGridElBoundingRect.width / hoverWidth);
const transformedHoverY = hoverY * (chartGridElBoundingRect.height / hoverHeight);
closest = this.closestInMultiArray(
transformedHoverX,
transformedHoverY,
seriesXValArr,
seriesYValArr
);
capturedSeries = closest.index;
j = (_a = closest.j) != null ? _a : 0;
if (capturedSeries !== null && w.globals.hasNullValues) {
seriesXValArr = w.globals.seriesXvalues[capturedSeries];
closest = this.closestInArray(transformedHoverX, seriesXValArr);
j = (_b = closest.j) != null ? _b : 0;
}
}
w.interact.capturedSeriesIndex = capturedSeries === null ? -1 : capturedSeries;
if (!j || j < 1) j = 0;
if (w.globals.isBarHorizontal) {
w.interact.capturedDataPointIndex = jHorz;
} else {
w.interact.capturedDataPointIndex = j;
}
return {
capturedSeries,
j: w.globals.isBarHorizontal ? jHorz : j,
hoverX,
hoverY
};
}
/**
* @param {any[]} Xarrays
*/
getFirstActiveXArray(Xarrays) {
const w = this.w;
let activeIndex = 0;
const firstActiveSeriesIndex = Xarrays.map(
(xarr, index) => {
return xarr.length > 0 ? index : -1;
}
);
for (let a = 0; a < firstActiveSeriesIndex.length; a++) {
if (firstActiveSeriesIndex[a] !== -1 && w.globals.collapsedSeriesIndices.indexOf(a) === -1 && w.globals.ancillaryCollapsedSeriesIndices.indexOf(a) === -1) {
activeIndex = firstActiveSeriesIndex[a];
break;
}
}
return activeIndex;
}
/**
* @param {number} hoverX
* @param {number} hoverY
* @param {any[]} Xarrays
* @param {any[]} Yarrays
*/
closestInMultiArray(hoverX, hoverY, Xarrays, Yarrays) {
const w = this.w;
const isActiveSeries = (seriesIndex) => {
return w.globals.collapsedSeriesIndices.indexOf(seriesIndex) === -1 && w.globals.ancillaryCollapsedSeriesIndices.indexOf(seriesIndex) === -1;
};
const chartType = w.config.chart.type;
const useSegmentDistance = !w.globals.comboCharts && (chartType === "line" || chartType === "area");
let closestDist = Infinity;
let closestSeriesIndex = null;
let closestPointIndex = null;
const ignoreY = w.config.tooltip.shared && w.globals.allSeriesHasEqualX && this.hasBars();
for (let i = 0; i < Xarrays.length; i++) {
if (!isActiveSeries(i)) {
continue;
}
const xArr = Xarrays[i];
const yArr = Yarrays[i];
const len = Math.min(xArr.length, yArr.length);
if (useSegmentDistance && len >= 2) {
for (let j = 0; j < len - 1; j++) {
const seg = this._distanceToSegment(
hoverX,
hoverY,
xArr[j],
yArr[j],
xArr[j + 1],
yArr[j + 1]
);
if (seg.dist < closestDist) {
closestDist = seg.dist;
closestSeriesIndex = i;
closestPointIndex = seg.t < 0.5 ? j : j + 1;
}
}
continue;
}
for (let j = 0; j < len; j++) {
const xVal = xArr[j];
const distX = hoverX - xVal;
let dist = Math.sqrt(distX * distX);
if (!ignoreY) {
const yVal = yArr[j];
const distY = hoverY - yVal;
dist = Math.sqrt(distX * distX + distY * distY);
}
if (dist < closestDist) {
closestDist = dist;
closestSeriesIndex = i;
closestPointIndex = j;
}
}
}
return {
index: closestSeriesIndex,
j: closestPointIndex
};
}
/**
* Perpendicular distance from point (px, py) to the line segment
* (ax, ay) → (bx, by). Returns the distance plus the projection
* parameter t (0 = at A, 1 = at B, clamped) so callers know which
* endpoint the projection landed nearest.
* @param {number} px
* @param {number} py
* @param {number} ax
* @param {number} ay
* @param {number} bx
* @param {number} by
* @returns {{ dist: number, t: number }}
*/
_distanceToSegment(px, py, ax, ay, bx, by) {
const dx = bx - ax;
const dy = by - ay;
const lenSq = dx * dx + dy * dy;
let t = lenSq === 0 ? 0 : ((px - ax) * dx + (py - ay) * dy) / lenSq;
if (t < 0) t = 0;
else if (t > 1) t = 1;
const cx = ax + t * dx;
const cy = ay + t * dy;
const ex = px - cx;
const ey = py - cy;
return { dist: Math.sqrt(ex * ex + ey * ey), t };
}
/**
* @param {number} val
* @param {any[]} arr
*/
closestInArray(val, arr) {
const curr = arr[0];
let currIndex = null;
let diff = Math.abs(val - curr);
for (let i = 0; i < arr.length; i++) {
const newdiff = Math.abs(val - arr[i]);
if (newdiff < diff) {
diff = newdiff;
currIndex = i;
}
}
return {
j: currIndex
};
}
/**
* When there are multiple series, it is possible to have different x values for each series.
* But it may be possible in those multiple series, that there is same x value for 2 or more
* series.
* @memberof Utils
* @param {number} j - the inner index of series (series[i][j])
* @return {boolean}
*/
isXoverlap(j) {
const w = this.w;
const xSameForAllSeriesJArr = [];
const seriesX = w.seriesData.seriesX.filter(
(s) => typeof s[0] !== "undefined"
);
if (seriesX.length > 0) {
for (let i = 0; i < seriesX.length - 1; i++) {
if (typeof seriesX[i][j] !== "undefined" && typeof seriesX[i + 1][j] !== "undefined") {
if (seriesX[i][j] !== seriesX[i + 1][j]) {
xSameForAllSeriesJArr.push("unEqual");
}
}
}
}
if (xSameForAllSeriesJArr.length === 0) {
return true;
}
return false;
}
isInitialSeriesSameLen() {
var _a, _b, _c;
let sameLen = true;
const initialSeries = (
/** @type {any[]} */
((_a = this.w.globals.initialSeries) == null ? void 0 : _a.filter(
/**
* @param {Record<string, any>} s
* @param {number} i
*/
(s, i) => {
var _a2;
return !((_a2 = this.w.globals.collapsedSeriesIndices) == null ? void 0 : _a2.includes(i));
}
)) || []
);
for (let i = 0; i < initialSeries.length - 1; i++) {
if (!((_b = initialSeries[i]) == null ? void 0 : _b.data) || !((_c = initialSeries[i + 1]) == null ? void 0 : _c.data)) return true;
if (initialSeries[i].data.length !== initialSeries[i + 1].data.length) {
sameLen = false;
break;
}
}
return sameLen;
}
/**
* @param {any[]} allbars
*/
getBarsHeight(allbars) {
const bars = [...allbars];
const totalHeight = bars.reduce((acc, bar) => acc + bar.getBBox().height, 0);
return totalHeight;
}
/**
* @param {number} capturedSeries
*/
getElMarkers(capturedSeries) {
if (typeof capturedSeries == "number") {
return this.w.dom.baseEl.querySelectorAll(
`.apexcharts-series[data\\:realIndex='${capturedSeries}'] .apexcharts-series-markers-wrap > *`
);
}
return this.w.dom.baseEl.querySelectorAll(
".apexcharts-series-markers-wrap > *"
);
}
getAllMarkers(filterCollapsed = false) {
let markersWraps = (
/** @type {any[]} */
[
...this.w.dom.baseEl.querySelectorAll(".apexcharts-series-markers-wrap")
]
);
if (filterCollapsed) {
markersWraps = markersWraps.filter((m) => {
const realIndex = Number(m.getAttribute("data:realIndex"));
return this.w.globals.collapsedSeriesIndices.indexOf(realIndex) === -1;
});
}
markersWraps.sort((a, b) => {
var indexA = Number(a.getAttribute("data:realIndex"));
var indexB = Number(b.getAttribute("data:realIndex"));
return indexB < indexA ? 1 : indexB > indexA ? -1 : 0;
});
const markers = [];
markersWraps.forEach((m) => {
markers.push(m.querySelector(".apexcharts-marker"));
});
return markers;
}
/**
* @param {number} capturedSeries
*/
hasMarkers(capturedSeries) {
const markers = this.getElMarkers(capturedSeries);
return markers.length > 0;
}
/**
* @param {any} point
* @param {number} size
*/
getPathFromPoint(point, size) {
const cx = Number(point.getAttribute("cx"));
const cy = Number(point.getAttribute("cy"));
const shape = point.getAttribute("shape");
return new Graphics(this.w).getMarkerPath(cx, cy, shape, size);
}
getElBars() {
return this.w.dom.baseEl.querySelectorAll(
".apexcharts-bar-series, .apexcharts-candlestick-series, .apexcharts-boxPlot-series, .apexcharts-rangebar-series"
);
}
hasBars() {
const bars = this.getElBars();
return bars.length > 0;
}
/**
* @param {number} index
*/
getHoverMarkerSize(index) {
const w = this.w;
let hoverSize = w.config.markers.hover.size;
if (hoverSize === void 0) {
hoverSize = w.globals.markers.size[index] + w.config.markers.hover.sizeOffset;
}
return hoverSize;
}
/**
* @param {string} state
*/
toggleAllTooltipSeriesGroups(state) {
const w = this.w;
const ttCtx = this.ttCtx;
if (ttCtx.allTooltipSeriesGroups.length === 0) {
ttCtx.allTooltipSeriesGroups = w.dom.baseEl.querySelectorAll(
".apexcharts-tooltip-series-group"
);
}
const allTooltipSeriesGroups = ttCtx.allTooltipSeriesGroups;
for (let i = 0; i < allTooltipSeriesGroups.length; i++) {
if (state === "enable") {
allTooltipSeriesGroups[i].classList.add("apexcharts-active");
allTooltipSeriesGroups[i].style.display = w.config.tooltip.items.display;
} else {
allTooltipSeriesGroups[i].classList.remove("apexcharts-active");
allTooltipSeriesGroups[i].style.display = "none";
}
}
}
}
class Labels {
/**
* @param {import('./Tooltip').default} tooltipContext
*/
constructor(tooltipContext) {
this.w = tooltipContext.w;
this.ttCtx = tooltipContext;
this.tooltipUtil = new Utils2(tooltipContext);
}
/** @param {{ shared?: boolean, ttItems?: any, i?: number, j?: any, y1?: any, y2?: any, e?: any }} opts */
drawSeriesTexts({ shared = true, ttItems, i = 0, j = null, y1, y2, e }) {
const w = this.w;
if (w.config.tooltip.custom !== void 0) {
this.handleCustomTooltip({ i, j, y1, y2, w });
} else {
this.toggleActiveInactiveSeries(shared, i);
}
const values = this.getValuesToPrint({
i,
j
});
this.printLabels({
i,
j,
values,
ttItems,
shared,
e
});
const tooltipEl = this.ttCtx.getElTooltip();
if (tooltipEl) {
this.ttCtx.tooltipRect.ttWidth = tooltipEl.getBoundingClientRect().width;
this.ttCtx.tooltipRect.ttHeight = tooltipEl.getBoundingClientRect().height;
}
}
/** @param {{i: any, j: any, values: any, ttItems: any, shared: any, e: any}} opts */
printLabels({ i, j, values, ttItems, shared, e }) {
var _a;
const w = this.w;
let val;
let goalVals = (
/** @type {any[]} */
[]
);
const hasGoalValues = (gi) => {
return w.seriesData.seriesGoals[gi] && w.seriesData.seriesGoals[gi][j] && Array.isArray(w.seriesData.seriesGoals[gi][j]);
};
const { xVal, zVal, xAxisTTVal } = values;
let seriesName = "";
let pColor = w.globals.colors[i];
if (j !== null && w.config.plotOptions.bar.distributed) {
pColor = w.globals.colors[j];
}
for (let t = 0, inverset = w.seriesData.series.length - 1; t < w.seriesData.series.length; t++, inverset--) {
let f = this.getFormatters(i);
seriesName = this.getSeriesName({
fn: f.yLbTitleFormatter,
index: i,
seriesIndex: i,
j
});
if (w.config.chart.type === "treemap") {
seriesName = f.yLbTitleFormatter(
String(
/** @type {any} */
w.config.series[i].data[j].x
),
{
series: w.seriesData.series,
seriesIndex: i,
dataPointIndex: j,
w
}
);
}
const tIndex = w.config.tooltip.inverseOrder ? inverset : t;
if (w.globals.axisCharts) {
const getValBySeriesIndex = (index) => {
var _a2, _b, _c, _d;
if (w.axisFlags.isRangeData) {
return f.yLbFormatter((_b = (_a2 = w.rangeData.seriesRangeStart) == null ? void 0 : _a2[index]) == null ? void 0 : _b[j], {
series: w.rangeData.seriesRangeStart,
seriesIndex: index,
dataPointIndex: j,
w
}) + " - " + f.yLbFormatter((_d = (_c = w.rangeData.seriesRangeEnd) == null ? void 0 : _c[index]) == null ? void 0 : _d[j], {
series: w.rangeData.seriesRangeEnd,
seriesIndex: index,
dataPointIndex: j,
w
});
}
return f.yLbFormatter(w.seriesData.series[index][j], {
series: w.seriesData.series,
seriesIndex: index,
dataPointIndex: j,
w
});
};
if (shared) {
f = this.getFormatters(tIndex);
seriesName = this.getSeriesName({
fn: f.yLbTitleFormatter,
index: tIndex,
seriesIndex: i,
j
});
pColor = w.globals.colors[tIndex];
val = getValBySeriesIndex(tIndex);
if (hasGoalValues(tIndex)) {
goalVals = w.seriesData.seriesGoals[tIndex][j].map(
(goal) => {
return {
attrs: goal,
val: f.yLbFormatter(goal.value, {
seriesIndex: tIndex,
dataPointIndex: j,
w
})
};
}
);
}
} else {
const targetFill = (_a = e == null ? void 0 : e.target) == null ? void 0 : _a.getAttribute("fill");
if (targetFill) {
if (targetFill.indexOf("url") !== -1) {
if (targetFill.indexOf("Pattern") !== -1) {
pColor = w.dom.baseEl.querySelector(targetFill.substr(4).slice(0, -1)).childNodes[0].getAttribute("stroke");
}
} else {
pColor = targetFill;
}
}
val = getValBySeriesIndex(i);
if (hasGoalValues(i) && Array.isArray(w.seriesData.seriesGoals[i][j])) {
goalVals = w.seriesData.seriesGoals[i][j].map(
(goal) => {
return {
attrs: goal,
val: f.yLbFormatter(goal.value, {
seriesIndex: i,
dataPointIndex: j,
w
})
};
}
);
}
}
}
if (j === null) {
val = f.yLbFormatter(w.seriesData.series[i], __spreadProps(__spreadValues({}, w), {
seriesIndex: i,
dataPointIndex: i
}));
}
this.DOMHandling({
i,
t: tIndex,
j,
ttItems,
values: {
val,
goalVals,
xVal,
xAxisTTVal,
zVal
},
seriesName,
shared,
pColor
});
}
}
/**
* @param {number} i
*/
getFormatters(i) {
const w = this.w;
let yLbFormatter = w.formatters.yLabelFormatters[i];
let yLbTitleFormatter;
if (w.formatters.ttVal !== void 0) {
if (Array.isArray(w.formatters.ttVal)) {
yLbFormatter = /** @type {any} */
w.formatters.ttVal[i] && /** @type {any} */
w.formatters.ttVal[i].formatter;
yLbTitleFormatter = /** @type {any} */
w.formatters.ttVal[i] && /** @type {any} */
w.formatters.ttVal[i].title && /** @type {any} */
w.formatters.ttVal[i].title.formatter;
} else {
yLbFormatter = /** @type {any} */
w.formatters.ttVal.formatter;
if (typeof /** @type {any} */
w.formatters.ttVal.title.formatter === "function") {
yLbTitleFormatter = /** @type {any} */
w.formatters.ttVal.title.formatter;
}
}
} else {
yLbTitleFormatter = w.config.tooltip.y.title.formatter;
}
if (typeof yLbFormatter !== "function") {
if (w.formatters.yLabelFormatters[0]) {
yLbFormatter = w.formatters.yLabelFormatters[0];
} else {
yLbFormatter = function(label) {
return label;
};
}
}
if (typeof yLbTitleFormatter !== "function") {
yLbTitleFormatter = function(label) {
return label ? label + ": " : "";
};
}
return {
yLbFormatter,
yLbTitleFormatter
};
}
/** @param {{fn: any, index: any, seriesIndex: any, j: any}} opts */
getSeriesName({ fn, index, seriesIndex, j }) {
const w = this.w;
return fn(String(w.seriesData.seriesNames[index]), {
series: w.seriesData.series,
seriesIndex,
dataPointIndex: j,
w
});
}
/** @param {{ t?: any, j?: any, i?: any, ttItems?: any, values?: any, seriesName?: any, shared?: any, pColor?: any }} opts */
DOMHandling({ t, j, ttItems, values, seriesName, shared, pColor }) {
const w = this.w;
const ttCtx = this.ttCtx;
const { val, goalVals, xVal, xAxisTTVal, zVal } = values;
let ttItemsChildren = null;
ttItemsChildren = ttItems[t].children;
if (w.config.tooltip.fillSeriesColor) {
ttItems[t].style.backgroundColor = pColor;
ttItemsChildren[0].style.display = "none";
}
if (ttCtx.showTooltipTitle) {
if (ttCtx.tooltipTitle === null) {
ttCtx.tooltipTitle = w.dom.baseEl.querySelector(
".apexcharts-tooltip-title"
);
}
if (ttCtx.tooltipTitle) {
ttCtx.tooltipTitle.innerHTML = xVal;
}
}
if (ttCtx.isXAxisTooltipEnabled) {
if (ttCtx.xaxisTooltipText) {
ttCtx.xaxisTooltipText.innerHTML = xAxisTTVal !== "" ? xAxisTTVal : xVal;
}
}
const ttYLabel = ttItems[t].querySelector(
".apexcharts-tooltip-text-y-label"
);
if (ttYLabel) {
ttYLabel.innerHTML = seriesName ? seriesName : "";
}
const ttYVal = ttItems[t].querySelector(".apexcharts-tooltip-text-y-value");
if (ttYVal) {
ttYVal.innerHTML = typeof val !== "undefined" ? val : "";
}
if (ttItemsChildren[0] && ttItemsChildren[0].classList.contains("apexcharts-tooltip-marker")) {
if (w.config.tooltip.marker.fillColors && Array.isArray(w.config.tooltip.marker.fillColors)) {
pColor = w.config.tooltip.marker.fillColors[t];
}
if (w.config.tooltip.fillSeriesColor) {
ttItemsChildren[0].style.backgroundColor = pColor;
} else {
ttItemsChildren[0].style.color = pColor;
}
}
if (!w.config.tooltip.marker.show) {
ttItemsChildren[0].style.display = "none";
}
const ttGLabel = ttItems[t].querySelector(
".apexcharts-tooltip-text-goals-label"
);
const ttGVal = ttItems[t].querySelector(
".apexcharts-tooltip-text-goals-value"
);
if (goalVals.length && w.seriesData.seriesGoals[t]) {
const createGoalsHtml = () => {
let gLabels = "<div>";
let gVals = "<div>";
goalVals.forEach((goal) => {
gLabels += ` <div style="display: flex"><span class="apexcharts-tooltip-marker" style="background-color: ${goal.attrs.strokeColor}; height: 3px; border-radius: 0; top: 5px;"></span> ${goal.attrs.name}</div>`;
gVals += `<div>${goal.val}</div>`;
});
ttGLabel.innerHTML = gLabels + `</div>`;
ttGVal.innerHTML = gVals + `</div>`;
};
if (shared) {
if (w.seriesData.seriesGoals[t][j] && Array.isArray(w.seriesData.seriesGoals[t][j])) {
createGoalsHtml();
} else {
ttGLabel.innerHTML = "";
ttGVal.innerHTML = "";
}
} else {
createGoalsHtml();
}
} else {
ttGLabel.innerHTML = "";
ttGVal.innerHTML = "";
}
if (zVal !== null) {
const ttZLabel = ttItems[t].querySelector(
".apexcharts-tooltip-text-z-label"
);
ttZLabel.innerHTML = w.config.tooltip.z.title;
const ttZVal = ttItems[t].querySelector(
".apexcharts-tooltip-text-z-value"
);
ttZVal.innerHTML = typeof zVal !== "undefined" ? zVal : "";
}
if (shared && ttItemsChildren[0]) {
if (w.config.tooltip.hideEmptySeries) {
const ttItemMarker = ttItems[t].querySelector(
".apexcharts-tooltip-marker"
);
const ttItemText = ttItems[t].querySelector(".apexcharts-tooltip-text");
if (parseFloat(val) == 0) {
ttItemMarker.style.display = "none";
ttItemText.style.display = "none";
} else {
ttItemMarker.style.display = "block";
ttItemText.style.display = "block";
}
}
if (typeof val === "undefined" || val === null || w.globals.ancillaryCollapsedSeriesIndices.indexOf(t) > -1 || w.globals.collapsedSeriesIndices.indexOf(t) > -1 || Array.isArray(ttCtx.tConfig.enabledOnSeries) && ttCtx.tConfig.enabledOnSeries.indexOf(t) === -1) {
ttItemsChildren[0].parentNode.style.display = "none";
} else {
ttItemsChildren[0].parentNode.style.display = w.config.tooltip.items.display;
}
} else {
if (Array.isArray(ttCtx.tConfig.enabledOnSeries) && ttCtx.tConfig.enabledOnSeries.indexOf(t) === -1) {
ttItemsChildren[0].parentNode.style.display = "none";
}
}
}
/**
* @param {boolean} shared
* @param {number} i
*/
toggleActiveInactiveSeries(shared, i) {
const w = this.w;
if (shared) {
this.tooltipUtil.toggleAllTooltipSeriesGroups("enable");
} else {
this.tooltipUtil.toggleAllTooltipSeriesGroups("disable");
const firstTooltipSeriesGroup = w.dom.baseEl.querySelector(
`.apexcharts-tooltip-series-group-${i}`
);
if (firstTooltipSeriesGroup) {
const ftsGroup = (
/** @type {HTMLElement} */
firstTooltipSeriesGroup
);
ftsGroup.classList.add("apexcharts-active");
ftsGroup.style.display = w.config.tooltip.items.display;
}
}
}
/** @param {{i: any, j: any}} opts */
getValuesToPrint({ i, j }) {
var _a, _b, _c, _d, _e, _f, _g, _h;
const w = this.w;
const filteredSeriesX = w.seriesData.seriesX.map(
(ser) => ser.length > 0 ? ser : []
);
let xVal = "";
let xAxisTTVal = "";
let zVal = null;
let val = null;
const customFormatterOpts = {
series: w.seriesData.series,
seriesIndex: i,
dataPointIndex: j,
w
};
const zFormatter = w.formatters.ttZFormatter;
if (j === null) {
val = w.seriesData.series[i];
} else {
if (w.axisFlags.isXNumeric && w.config.chart.type !== "treemap") {
xVal = filteredSeriesX[i][j];
if (filteredSeriesX[i].length === 0) {
const firstActiveSeriesIndex = this.tooltipUtil.getFirstActiveXArray(filteredSeriesX);
xVal = filteredSeriesX[firstActiveSeriesIndex][j];
}
} else {
const dataFormat = new Data(this.w);
if (dataFormat.isFormatXY()) {
xVal = typeof /** @type {any} */
w.config.series[i].data[j] !== "undefined" ? (
/** @type {any} */
w.config.series[i].data[j].x
) : "";
} else {
xVal = typeof w.labelData.labels[j] !== "undefined" ? w.labelData.labels[j] : "";
}
}
}
const bufferXVal = xVal;
if (w.axisFlags.isXNumeric && w.config.xaxis.type === "datetime") {
const xFormat = new Formatters(this.w);
xVal = xFormat.xLabelFormat(
/** @type {Function} */
w.formatters.ttKeyFormatter,
bufferXVal,
bufferXVal,
{
i: void 0,
dateFormatter: new DateTime(this.w).formatDate,
w: this.w
}
);
} else {
if (w.globals.isBarHorizontal) {
xVal = w.formatters.yLabelFormatters[0](bufferXVal, customFormatterOpts);
} else {
xVal = (_c = (_b = (_a = w.formatters).xLabelFormatter) == null ? void 0 : _b.call(_a, bufferXVal, customFormatterOpts)) != null ? _c : bufferXVal;
}
}
if (w.config.tooltip.x.formatter !== void 0) {
xVal = (_f = (_e = (_d = w.formatters).ttKeyFormatter) == null ? void 0 : _e.call(_d, bufferXVal, customFormatterOpts)) != null ? _f : bufferXVal;
}
if (w.seriesData.seriesZ.length > 0 && w.seriesData.seriesZ[i].length > 0) {
zVal = zFormatter == null ? void 0 : zFormatter(w.seriesData.seriesZ[i][j], w);
}
if (typeof w.config.xaxis.tooltip.formatter === "function") {
xAxisTTVal = (_h = (_g = w.formatters).xaxisTooltipFormatter) == null ? void 0 : _h.call(
_g,
bufferXVal,
customFormatterOpts
);
} else {
xAxisTTVal = xVal;
}
return {
val: Array.isArray(val) ? val.join(" ") : val,
xVal: Array.isArray(xVal) ? xVal.join(" ") : xVal,
xAxisTTVal: Array.isArray(xAxisTTVal) ? xAxisTTVal.join(" ") : xAxisTTVal,
zVal
};
}
/** @param {{i: any, j: any, y1: any, y2: any, w: any}} opts */
handleCustomTooltip({ i, j, y1, y2, w }) {
const tooltipEl = this.ttCtx.getElTooltip();
let fn = w.config.tooltip.custom;
if (Array.isArray(fn) && fn[i]) {
fn = fn[i];
}
const customTooltip = fn({
series: w.seriesData.series,
seriesIndex: i,
dataPointIndex: j,
y1,
y2,
w
});
if (tooltipEl) {
if (typeof customTooltip === "string" || typeof customTooltip === "number") {
tooltipEl.innerHTML = String(customTooltip);
} else if (customTooltip instanceof Element || typeof customTooltip.nodeName === "string") {
tooltipEl.innerHTML = "";
tooltipEl.appendChild(customTooltip.cloneNode(true));
}
}
}
}
class Position {
/**
* @param {import('./Tooltip').default} tooltipContext
*/
constructor(tooltipContext) {
this.ttCtx = tooltipContext;
this.w = tooltipContext.w;
}
/**
* This will move the crosshair (the vertical/horz line that moves along with mouse)
* Along with this, this function also calls the xaxisMove function
* @memberof Position
* @param {number} cx - point's x position, wherever point's x is, you need to move crosshair
* @param {number | null} [j]
*/
moveXCrosshairs(cx, j = null) {
const ttCtx = this.ttCtx;
const w = this.w;
const xcrosshairs = ttCtx.getElXCrosshairs();
let x = cx - ttCtx.xcrosshairsWidth / 2;
const tickAmount = w.labelData.labels.slice().length;
if (j !== null) {
x = w.layout.gridWidth / tickAmount * j;
}
if (xcrosshairs !== null && !w.globals.isBarHorizontal) {
xcrosshairs.setAttribute("x", String(x));
xcrosshairs.setAttribute("x1", String(x));
xcrosshairs.setAttribute("x2", String(x));
xcrosshairs.setAttribute("y2", String(w.layout.gridHeight));
xcrosshairs.classList.add("apexcharts-active");
}
if (x < 0) {
x = 0;
}
if (x > w.layout.gridWidth) {
x = w.layout.gridWidth;
}
if (ttCtx.isXAxisTooltipEnabled) {
let tx = x;
if (w.config.xaxis.crosshairs.width === "tickWidth" || w.config.xaxis.crosshairs.width === "barWidth") {
tx = x + ttCtx.xcrosshairsWidth / 2;
}
this.moveXAxisTooltip(tx);
}
}
/**
* This will move the crosshair (the vertical/horz line that moves along with mouse)
* Along with this, this function also calls the xaxisMove function
* @memberof Position
* @param {number} cy - point's y position, wherever point's y is, you need to move crosshair
*/
moveYCrosshairs(cy) {
const ttCtx = this.ttCtx;
if (ttCtx.ycrosshairs !== null) {
Graphics.setAttrs(ttCtx.ycrosshairs, {
y1: cy,
y2: cy
});
}
if (ttCtx.ycrosshairsHidden !== null) {
Graphics.setAttrs(ttCtx.ycrosshairsHidden, {
y1: cy,
y2: cy
});
}
}
/**
** AxisTooltip is the small rectangle which appears on x axis with x value, when user moves
* @memberof Position
* @param {number} cx - point's x position, wherever point's x is, you need to move
*/
moveXAxisTooltip(cx) {
var _a, _b;
const w = this.w;
const ttCtx = this.ttCtx;
if (ttCtx.xaxisTooltip !== null && ttCtx.xcrosshairsWidth !== 0) {
ttCtx.xaxisTooltip.classList.add("apexcharts-active");
const cy = ttCtx.xaxisOffY + w.config.xaxis.tooltip.offsetY + w.layout.translateY + 1 + w.config.xaxis.offsetY;
const xaxisTTText = ttCtx.xaxisTooltip.getBoundingClientRect();
const xaxisTTTextWidth = xaxisTTText.width;
cx = cx - xaxisTTTextWidth / 2;
if (!isNaN(cx)) {
cx = cx + w.layout.translateX;
const graphics = new Graphics(this.w);
const textRect = graphics.getTextRects(
(_b = (_a = ttCtx.xaxisTooltipText) == null ? void 0 : _a.innerHTML) != null ? _b : "",
w.config.xaxis.labels.style.fontSize
);
if (ttCtx.xaxisTooltipText) {
ttCtx.xaxisTooltipText.style.minWidth = textRect.width + "px";
}
ttCtx.xaxisTooltip.style.left = cx + "px";
ttCtx.xaxisTooltip.style.top = cy + "px";
}
}
}
/**
* @param {number} index
*/
moveYAxisTooltip(index) {
var _a, _b;
const w = this.w;
const ttCtx = this.ttCtx;
if (ttCtx.yaxisTTEls === null) {
ttCtx.yaxisTTEls = /** @type {any[]} */
[
...w.dom.baseEl.querySelectorAll(".apexcharts-yaxistooltip")
];
}
const ycrosshairsHiddenRectY1 = parseInt(
(_b = (_a = ttCtx.ycrosshairsHidden) == null ? void 0 : _a.getAttribute("y1")) != null ? _b : "0",
10
);
let cy = w.layout.translateY + ycrosshairsHiddenRectY1;
if (ttCtx.yaxisTTEls) {
const yAxisTTRect = ttCtx.yaxisTTEls[index].getBoundingClientRect();
const yAxisTTHeight = yAxisTTRect.height;
let cx = w.globals.translateYAxisX[index] - 2;
if (w.config.yaxis[index].opposite) {
cx = cx - yAxisTTRect.width;
}
cy = cy - yAxisTTHeight / 2;
if (w.globals.ignoreYAxisIndexes.indexOf(index) === -1 && cy > 0 && cy < w.layout.gridHeight) {
ttCtx.yaxisTTEls[index].classList.add("apexcharts-active");
ttCtx.yaxisTTEls[index].style.top = cy + "px";
ttCtx.yaxisTTEls[index].style.left = cx + w.config.yaxis[index].tooltip.offsetX + "px";
} else {
ttCtx.yaxisTTEls[index].classList.remove("apexcharts-active");
}
}
}
/**
** moves the whole tooltip by changing x, y attrs
* @memberof Position
* @param {number} cx - point's x position, wherever point's x is, you need to move tooltip
* @param {number} cy - point's y position, wherever point's y is, you need to move tooltip
* @param {number | null} [markerSize] - point's size
*/
moveTooltip(cx, cy, markerSize = null) {
var _a, _b, _c, _d, _e, _f, _g;
const w = this.w;
const ttCtx = this.ttCtx;
const tooltipEl = ttCtx.getElTooltip();
const tooltipRect = ttCtx.tooltipRect;
const pointSize = markerSize !== null ? parseFloat(String(markerSize)) : 1;
let x = parseFloat(String(cx)) + pointSize + 5;
let y = parseFloat(String(cy)) + pointSize / 2;
if (x > w.layout.gridWidth / 2) {
x = x - tooltipRect.ttWidth - pointSize - 10;
}
if (x > w.layout.gridWidth - tooltipRect.ttWidth - 10) {
x = w.layout.gridWidth - tooltipRect.ttWidth;
}
if (x < -20) {
x = -20;
}
if (w.config.tooltip.followCursor) {
const elGrid = ttCtx.getElGrid();
if (!elGrid) return;
const seriesBound = elGrid.getBoundingClientRect();
x = ttCtx.e.clientX - seriesBound.left;
if (x > w.layout.gridWidth / 2) {
x = x - ttCtx.tooltipRect.ttWidth;
}
y = ttCtx.e.clientY + w.layout.translateY - seriesBound.top;
if (y > w.layout.gridHeight / 2) {
y = y - ttCtx.tooltipRect.ttHeight;
}
} else {
if (!w.globals.isBarHorizontal) {
if (tooltipRect.ttHeight / 2 + y > w.layout.gridHeight) {
y = w.layout.gridHeight - tooltipRect.ttHeight + w.layout.translateY;
}
}
}
if (!isNaN(x)) {
x = x + w.layout.translateX;
const a11y = (_b = (_a = w.config) == null ? void 0 : _a.chart) == null ? void 0 : _b.accessibility;
if ((a11y == null ? void 0 : a11y.enabled) && ((_d = (_c = a11y == null ? void 0 : a11y.keyboard) == null ? void 0 : _c.navigation) == null ? void 0 : _d.enabled) && ((_g = (_f = (_e = w.dom) == null ? void 0 : _e.baseEl) == null ? void 0 : _f.querySelector) == null ? void 0 : _g.call(_f, ".apexcharts-keyboard-focused"))) {
const cyNum = parseFloat(String(cy));
const ttH = tooltipRect.ttHeight || 0;
const margin = (pointSize || 1) + 12;
const tooltipTop = y;
const tooltipBottom = y + ttH;
if (!isNaN(cyNum) && ttH > 0 && tooltipTop < cyNum + margin && tooltipBottom > cyNum - margin) {
y = cyNum - ttH - margin;
if (y < 0) {
y = cyNum + margin;
}
}
}
if (tooltipEl) {
tooltipEl.style.left = x + "px";
tooltipEl.style.top = y + "px";
}
}
}
/**
* @param {number} i
* @param {number} j
*/
moveMarkers(i, j) {
var _a;
const w = this.w;
const ttCtx = this.ttCtx;
if (w.globals.markers.size[i] > 0) {
const allPoints = w.dom.baseEl.querySelectorAll(
` .apexcharts-series[data\\:realIndex='${i}'] .apexcharts-marker`
);
for (let p = 0; p < allPoints.length; p++) {
if (parseInt((_a = allPoints[p].getAttribute("rel")) != null ? _a : "0", 10) === j) {
ttCtx.marker.resetPointsSize();
ttCtx.marker.enlargeCurrentPoint(j, allPoints[p]);
}
}
} else {
ttCtx.marker.resetPointsSize();
this.moveDynamicPointOnHover(j, i);
}
}
// This function is used when you need to show markers/points only on hover -
// DIFFERENT X VALUES in multiple series
/**
* @param {number} j
* @param {number} capturedSeries
*/
moveDynamicPointOnHover(j, capturedSeries) {
var _a, _b, _c, _d, _e;
const w = this.w;
const ttCtx = this.ttCtx;
let cx = 0;
let cy = 0;
const graphics = new Graphics(this.w);
const pointsArr = w.globals.pointsArray;
const hoverSize = ttCtx.tooltipUtil.getHoverMarkerSize(capturedSeries);
const serType = (
/** @type {any} */
w.config.series[capturedSeries].type
);
if (serType && (serType === "column" || serType === "candlestick" || serType === "boxPlot")) {
return;
}
cx = (_b = (_a = pointsArr[capturedSeries]) == null ? void 0 : _a[j]) == null ? void 0 : _b[0];
cy = ((_d = (_c = pointsArr[capturedSeries]) == null ? void 0 : _c[j]) == null ? void 0 : _d[1]) || 0;
const point = w.dom.baseEl.querySelector(
`.apexcharts-series[data\\:realIndex='${capturedSeries}'] .apexcharts-series-markers path`
);
if (point && cy < w.layout.gridHeight && cy > 0) {
const shape = (_e = point.getAttribute("shape")) != null ? _e : "circle";
const path = graphics.getMarkerPath(cx, cy, shape, hoverSize * 1.5);
point.setAttribute("d", path);
}
this.moveXCrosshairs(cx);
if (!ttCtx.fixedTooltip) {
this.moveTooltip(cx, cy, hoverSize);
}
}
// This function is used when you need to show markers/points only on hover -
// SAME X VALUES in multiple series
/**
* @param {number} j
*/
moveDynamicPointsOnHover(j) {
var _a, _b;
const ttCtx = this.ttCtx;
const w = ttCtx.w;
let cx = 0;
let cy = 0;
let activeSeries = 0;
const pointsArr = w.globals.pointsArray;
const series = new Series(this.w);
const graphics = new Graphics(this.w);
activeSeries = series.getActiveConfigSeriesIndex("asc", [
"line",
"area",
"scatter",
"bubble"
]);
const hoverSize = ttCtx.tooltipUtil.getHoverMarkerSize(activeSeries);
if ((_a = pointsArr[activeSeries]) == null ? void 0 : _a[j]) {
cx = pointsArr[activeSeries][j][0];
cy = pointsArr[activeSeries][j][1];
}
if (isNaN(cx)) {
return;
}
const points = ttCtx.tooltipUtil.getAllMarkers();
if (points.length) {
for (let p = 0; p < w.seriesData.series.length; p++) {
const pointArr = pointsArr[p];
if (w.globals.comboCharts) {
if (typeof pointArr === "undefined") {
points.splice(p, 0, null);
}
}
if (pointArr && pointArr.length) {
let pcy = pointsArr[p][j][1];
let pcy2;
points[p].setAttribute("cx", cx);
const shape = (_b = points[p].getAttribute("shape")) != null ? _b : "circle";
if (w.config.chart.type === "rangeArea" && !w.globals.comboCharts) {
const rangeStartIndex = j + w.seriesData.series[p].length;
pcy2 = pointsArr[p][rangeStartIndex][1];
const pcyDiff = Math.abs(pcy - pcy2) / 2;
pcy = pcy - pcyDiff;
}
if (pcy !== null && !isNaN(pcy) && pcy < w.layout.gridHeight + hoverSize && pcy + hoverSize > 0) {
const path = graphics.getMarkerPath(cx, pcy, shape, hoverSize);
points[p].setAttribute("d", path);
} else {
points[p].setAttribute("d", "");
}
}
}
}
this.moveXCrosshairs(cx);
if (!ttCtx.fixedTooltip) {
this.moveTooltip(cx, cy || w.layout.gridHeight, hoverSize);
}
}
/**
* @param {number} j
* @param {number} capturedSeries
*/
moveStickyTooltipOverBars(j, capturedSeries) {
var _a, _b, _c;
const w = this.w;
const ttCtx = this.ttCtx;
let barLen = w.globals.columnSeries ? (
/** @type {any} */
w.globals.columnSeries.length
) : w.seriesData.series.length;
if (w.config.chart.stacked) {
barLen = w.globals.barGroups.length;
}
let i = barLen >= 2 && barLen % 2 === 0 ? Math.floor(barLen / 2) : Math.floor(barLen / 2) + 1;
if (w.globals.isBarHorizontal) {
const series = new Series(this.w);
i = series.getActiveConfigSeriesIndex("desc") + 1;
}
let jBar = w.dom.baseEl.querySelector(
`.apexcharts-bar-series .apexcharts-series[rel='${i}'] path[j='${j}'], .apexcharts-candlestick-series .apexcharts-series[rel='${i}'] path[j='${j}'], .apexcharts-boxPlot-series .apexcharts-series[rel='${i}'] path[j='${j}'], .apexcharts-rangebar-series .apexcharts-series[rel='${i}'] path[j='${j}']`
);
if (!jBar && typeof capturedSeries === "number") {
jBar = w.dom.baseEl.querySelector(
`.apexcharts-bar-series .apexcharts-series[data\\:realIndex='${capturedSeries}'] path[j='${j}'],
.apexcharts-candlestick-series .apexcharts-series[data\\:realIndex='${capturedSeries}'] path[j='${j}'],
.apexcharts-boxPlot-series .apexcharts-series[data\\:realIndex='${capturedSeries}'] path[j='${j}'],
.apexcharts-rangebar-series .apexcharts-series[data\\:realIndex='${capturedSeries}'] path[j='${j}']`
);
}
let bcx = jBar ? parseFloat((_a = jBar.getAttribute("cx")) != null ? _a : "0") : 0;
let bcy = jBar ? parseFloat((_b = jBar.getAttribute("cy")) != null ? _b : "0") : 0;
const bw = jBar ? parseFloat((_c = jBar.getAttribute("barWidth")) != null ? _c : "0") : 0;
const elGrid = ttCtx.getElGrid();
if (!elGrid) return;
const seriesBound = elGrid.getBoundingClientRect();
const isBoxOrCandle = jBar && (jBar.classList.contains("apexcharts-candlestick-area") || jBar.classList.contains("apexcharts-boxPlot-area"));
if (w.axisFlags.isXNumeric) {
if (jBar && !isBoxOrCandle) {
bcx = bcx - (barLen % 2 !== 0 ? bw / 2 : 0);
}
if (jBar && // fixes apexcharts.js#2354
isBoxOrCandle) {
bcx = bcx - bw / 2;
}
} else {
if (!w.globals.isBarHorizontal) {
bcx = ttCtx.xAxisTicksPositions[j - 1] + ttCtx.dataPointsDividedWidth / 2;
if (isNaN(bcx)) {
bcx = ttCtx.xAxisTicksPositions[j] - ttCtx.dataPointsDividedWidth / 2;
}
}
}
if (!w.globals.isBarHorizontal) {
if (w.config.tooltip.followCursor) {
bcy = ttCtx.e.clientY - seriesBound.top - ttCtx.tooltipRect.ttHeight / 2;
} else {
if (bcy + ttCtx.tooltipRect.ttHeight + 15 > w.layout.gridHeight) {
bcy = w.layout.gridHeight;
}
}
} else {
bcy = bcy - ttCtx.tooltipRect.ttHeight;
}
if (!w.globals.isBarHorizontal) {
this.moveXCrosshairs(bcx);
}
if (!ttCtx.fixedTooltip) {
this.moveTooltip(bcx, bcy || w.layout.gridHeight);
}
}
}
class Marker {
/**
* @param {import('./Tooltip').default} tooltipContext
*/
constructor(tooltipContext) {
this.w = tooltipContext.w;
this.ttCtx = tooltipContext;
this.ctx = tooltipContext.ctx;
this.tooltipPosition = new Position(tooltipContext);
}
drawDynamicPoints() {
const w = this.w;
const graphics = new Graphics(this.w);
const marker = new Markers(this.w, this.ctx);
const elsSeries = (
/** @type {any[]} */
[
...w.dom.baseEl.querySelectorAll(".apexcharts-series")
]
);
if (w.config.chart.stacked) {
elsSeries.sort((a, b) => {
return parseFloat(a.getAttribute("data:realIndex")) - parseFloat(b.getAttribute("data:realIndex"));
});
}
for (let i = 0; i < elsSeries.length; i++) {
const pointsMain = elsSeries[i].querySelector(
`.apexcharts-series-markers-wrap`
);
if (pointsMain !== null) {
let PointClasses = `apexcharts-marker w${(Math.random() + 1).toString(36).substring(4)}`;
if ((w.config.chart.type === "line" || w.config.chart.type === "area") && !w.globals.comboCharts && !w.config.tooltip.intersect) {
PointClasses += " no-pointer-events";
}
const elPointOptions = marker.getMarkerConfig({
cssClass: PointClasses,
seriesIndex: Number(pointsMain.getAttribute("data:realIndex"))
// fixes apexcharts/apexcharts.js #1427
});
const point = graphics.drawMarker(0, 0, elPointOptions);
point.node.setAttribute("default-marker-size", 0);
const elPointsG = BrowserAPIs.createElementNS(SVGNS, "g");
elPointsG.classList.add("apexcharts-series-markers");
elPointsG.appendChild(point.node);
pointsMain.appendChild(elPointsG);
}
}
}
/**
* @param {any} rel
* @param {any} point
* @param {number | null} [x]
* @param {number | null} [y]
*/
enlargeCurrentPoint(rel, point, x = null, y = null) {
const w = this.w;
if (w.config.chart.type !== "bubble") {
this.newPointSize(rel, point);
}
let cx = point.getAttribute("cx");
let cy = point.getAttribute("cy");
if (x !== null && y !== null) {
cx = x;
cy = y;
}
this.tooltipPosition.moveXCrosshairs(cx);
if (!this.fixedTooltip) {
if (w.config.chart.type === "radar") {
const elGrid = this.ttCtx.getElGrid();
if (!elGrid) return;
const seriesBound = elGrid.getBoundingClientRect();
cx = this.ttCtx.e.clientX - seriesBound.left;
}
this.tooltipPosition.moveTooltip(cx, cy, w.config.markers.hover.size);
}
}
/**
* @param {number} j
*/
enlargePoints(j) {
var _a, _b;
const w = this.w;
const me = this;
const ttCtx = this.ttCtx;
const col = j;
const points = w.dom.baseEl.querySelectorAll(
".apexcharts-series:not(.apexcharts-series-collapsed) .apexcharts-marker"
);
let newSize = w.config.markers.hover.size;
for (let p = 0; p < points.length; p++) {
const rel = points[p].getAttribute("rel");
const index = points[p].getAttribute("index");
if (newSize === void 0) {
newSize = w.globals.markers.size[
/** @type {any} */
index
] + w.config.markers.hover.sizeOffset;
}
if (col === parseInt(rel != null ? rel : "0", 10)) {
me.newPointSize(col, points[p]);
const cx = (_a = points[p].getAttribute("cx")) != null ? _a : "0";
const cy = (_b = points[p].getAttribute("cy")) != null ? _b : "0";
me.tooltipPosition.moveXCrosshairs(parseFloat(cx));
if (!ttCtx.fixedTooltip) {
me.tooltipPosition.moveTooltip(
parseFloat(cx),
parseFloat(cy),
newSize
);
}
} else {
me.oldPointSize(points[p]);
}
}
}
/**
* @param {any} rel
* @param {any} point
*/
newPointSize(rel, point) {
const w = this.w;
let newSize = w.config.markers.hover.size;
const elPoint = rel === 0 ? point.parentNode.firstChild : point.parentNode.lastChild;
if (elPoint.getAttribute("default-marker-size") !== "0") {
const index = parseInt(elPoint.getAttribute("index"), 10);
if (newSize === void 0) {
newSize = w.globals.markers.size[index] + w.config.markers.hover.sizeOffset;
}
if (newSize < 0) {
newSize = 0;
}
const path = this.ttCtx.tooltipUtil.getPathFromPoint(point, newSize);
point.setAttribute("d", path);
}
}
/**
* @param {any} point
*/
oldPointSize(point) {
const size = parseFloat(point.getAttribute("default-marker-size"));
const path = this.ttCtx.tooltipUtil.getPathFromPoint(point, size);
point.setAttribute("d", path);
}
resetPointsSize() {
var _a;
const w = this.w;
const points = w.dom.baseEl.querySelectorAll(
".apexcharts-series:not(.apexcharts-series-collapsed) .apexcharts-marker"
);
for (let p = 0; p < points.length; p++) {
const size = parseFloat(
(_a = points[p].getAttribute("default-marker-size")) != null ? _a : "0"
);
if (Utils$1.isNumber(size) && size > 0) {
const path = this.ttCtx.tooltipUtil.getPathFromPoint(points[p], size);
points[p].setAttribute("d", path);
} else {
points[p].setAttribute("d", "M0,0");
}
}
}
}
class Intersect {
/**
* @param {import('./Tooltip').default} tooltipContext
*/
constructor(tooltipContext) {
this.w = tooltipContext.w;
const w = this.w;
this.ttCtx = tooltipContext;
this.isVerticalGroupedRangeBar = !w.globals.isBarHorizontal && w.config.chart.type === "rangeBar" && w.config.plotOptions.bar.rangeBarGroupRows;
}
// a helper function to get an element's attribute value
/**
* @param {Event} e
* @param {string} attr
*/
getAttr(e, attr) {
var _a;
return parseFloat(
/** @type {Element} */
(_a = e.target.getAttribute(attr)) != null ? _a : ""
);
}
// handle tooltip for heatmaps and treemaps
/** @param {{e: any, opt: any, x: any, y: any, type: any}} opts */
handleHeatTreeTooltip({ e, opt, x, y, type }) {
var _a, _b;
const ttCtx = this.ttCtx;
const w = this.w;
if (e.target.classList.contains(`apexcharts-${type}-rect`)) {
const i = this.getAttr(e, "i");
const j = this.getAttr(e, "j");
const cx = this.getAttr(e, "cx");
const cy = this.getAttr(e, "cy");
const width = this.getAttr(e, "width");
const height = this.getAttr(e, "height");
ttCtx.tooltipLabels.drawSeriesTexts({
ttItems: opt.ttItems,
i,
j,
shared: false,
e
});
w.interact.capturedSeriesIndex = i;
w.interact.capturedDataPointIndex = j;
x = cx + ttCtx.tooltipRect.ttWidth / 2 + width;
y = cy + ttCtx.tooltipRect.ttHeight / 2 - height / 2;
ttCtx.tooltipPosition.moveXCrosshairs(cx + width / 2);
if (x > w.layout.gridWidth / 2) {
x = cx - ttCtx.tooltipRect.ttWidth / 2 + width;
}
if (ttCtx.w.config.tooltip.followCursor) {
const seriesBound = w.dom.elWrap.getBoundingClientRect();
x = ((_a = w.interact.clientX) != null ? _a : 0) - seriesBound.left - (x > w.layout.gridWidth / 2 ? ttCtx.tooltipRect.ttWidth : 0);
y = ((_b = w.interact.clientY) != null ? _b : 0) - seriesBound.top - (y > w.layout.gridHeight / 2 ? ttCtx.tooltipRect.ttHeight : 0);
}
}
return {
x,
y
};
}
/**
* handle tooltips for line/area/scatter charts where tooltip.intersect is true
* when user hovers over the marker directly, this function is executed
*/
/** @param {{e: any, opt: any, x: any, y: any}} opts */
handleMarkerTooltip({ e, opt, x, y }) {
const w = this.w;
const ttCtx = this.ttCtx;
let i;
let j;
if (e.target.classList.contains("apexcharts-marker")) {
const cx = parseInt(opt.paths.getAttribute("cx"), 10);
const cy = parseInt(opt.paths.getAttribute("cy"), 10);
const val = parseFloat(opt.paths.getAttribute("val"));
j = parseInt(opt.paths.getAttribute("rel"), 10);
i = parseInt(
opt.paths.parentNode.parentNode.parentNode.getAttribute("rel"),
10
) - 1;
if (ttCtx.intersect) {
const el = Utils$1.findAncestor(opt.paths, "apexcharts-series");
if (el) {
i = parseInt(el.getAttribute("data:realIndex"), 10);
}
}
ttCtx.tooltipLabels.drawSeriesTexts({
ttItems: opt.ttItems,
i,
j,
shared: ttCtx.showOnIntersect ? false : w.config.tooltip.shared,
e
});
if (e.type === "mouseup") {
ttCtx.markerClick(e, i, j);
}
w.interact.capturedSeriesIndex = i;
w.interact.capturedDataPointIndex = j;
x = cx;
y = cy + w.layout.translateY - ttCtx.tooltipRect.ttHeight * 1.4;
if (ttCtx.w.config.tooltip.followCursor) {
const elGrid = ttCtx.getElGrid();
if (!elGrid) return { x, y };
const seriesBound = elGrid.getBoundingClientRect();
y = ttCtx.e.clientY + w.layout.translateY - seriesBound.top;
}
if (val < 0) {
y = cy;
}
ttCtx.marker.enlargeCurrentPoint(j, opt.paths, x, y);
}
return {
x,
y
};
}
/**
* handle tooltips for bar/column charts
*/
/** @param {{e: any, opt: any}} opts */
handleBarTooltip({ e, opt }) {
const w = this.w;
const ttCtx = this.ttCtx;
const tooltipEl = ttCtx.getElTooltip();
let bx = 0;
let x = 0;
let y = 0;
let i = 0;
let strokeWidth;
const barXY = this.getBarTooltipXY({
e,
opt
});
if (barXY.j === null && barXY.barHeight === 0 && barXY.barWidth === 0) {
return;
}
i = barXY.i;
const j = barXY.j;
w.interact.capturedSeriesIndex = i;
w.interact.capturedDataPointIndex = j !== null ? j : w.interact.capturedDataPointIndex;
if (w.globals.isBarHorizontal && ttCtx.tooltipUtil.hasBars() || !w.config.tooltip.shared) {
x = barXY.x;
y = barXY.y;
strokeWidth = Array.isArray(w.config.stroke.width) ? w.config.stroke.width[i] : w.config.stroke.width;
bx = x;
} else {
if (!w.globals.comboCharts && !w.config.tooltip.shared) {
bx = bx / 2;
}
}
if (isNaN(y)) {
y = w.globals.svgHeight - ttCtx.tooltipRect.ttHeight;
}
if (x + ttCtx.tooltipRect.ttWidth > w.layout.gridWidth) {
x = x - ttCtx.tooltipRect.ttWidth;
} else if (x < 0) {
x = 0;
}
if (ttCtx.w.config.tooltip.followCursor) {
const elGrid = ttCtx.getElGrid();
if (!elGrid) return;
}
if (ttCtx.tooltip === null) {
ttCtx.tooltip = w.dom.baseEl.querySelector(".apexcharts-tooltip");
}
if (!w.config.tooltip.shared) {
if (w.globals.comboBarCount > 0) {
ttCtx.tooltipPosition.moveXCrosshairs(bx + strokeWidth / 2);
} else {
ttCtx.tooltipPosition.moveXCrosshairs(bx);
}
}
if (!ttCtx.fixedTooltip && (!w.config.tooltip.shared || w.globals.isBarHorizontal && ttCtx.tooltipUtil.hasBars())) {
y = y + w.layout.translateY - ttCtx.tooltipRect.ttHeight / 2;
if (tooltipEl) {
tooltipEl.style.left = x + w.layout.translateX + "px";
tooltipEl.style.top = y + "px";
}
}
}
/** @param {{e: any, opt: any}} opts */
getBarTooltipXY({ e, opt }) {
const w = this.w;
let j = null;
const ttCtx = this.ttCtx;
let i = 0;
let x = 0;
let y = 0;
let barWidth = 0;
let barHeight = 0;
const cl = e.target.classList;
if (cl.contains("apexcharts-bar-area") || cl.contains("apexcharts-candlestick-area") || cl.contains("apexcharts-boxPlot-area") || cl.contains("apexcharts-rangebar-area")) {
const bar = e.target;
const barRect = bar.getBoundingClientRect();
const seriesBound = opt.elGrid.getBoundingClientRect();
const bh = barRect.height;
barHeight = barRect.height;
const bw = barRect.width;
const cx = parseInt(bar.getAttribute("cx"), 10);
const cy = parseInt(bar.getAttribute("cy"), 10);
barWidth = parseFloat(bar.getAttribute("barWidth"));
const clientX = e.type === "touchmove" ? e.touches[0].clientX : e.clientX;
j = parseInt(bar.getAttribute("j"), 10);
i = parseInt(bar.parentNode.getAttribute("rel"), 10) - 1;
const y1 = bar.getAttribute("data-range-y1");
const y2 = bar.getAttribute("data-range-y2");
if (w.globals.comboCharts) {
i = parseInt(bar.parentNode.getAttribute("data:realIndex"), 10);
}
const handleXForColumns = (x2) => {
if (w.axisFlags.isXNumeric) {
x2 = cx - bw / 2;
} else {
if (this.isVerticalGroupedRangeBar) {
x2 = cx + bw / 2;
} else {
x2 = cx - ttCtx.dataPointsDividedWidth + bw / 2;
}
}
return x2;
};
const handleYForBars = () => {
return cy - ttCtx.dataPointsDividedHeight + bh / 2 - ttCtx.tooltipRect.ttHeight / 2;
};
ttCtx.tooltipLabels.drawSeriesTexts({
ttItems: opt.ttItems,
i,
j,
y1: y1 ? parseInt(y1, 10) : null,
y2: y2 ? parseInt(y2, 10) : null,
shared: ttCtx.showOnIntersect ? false : w.config.tooltip.shared,
e
});
if (w.config.tooltip.followCursor) {
if (w.globals.isBarHorizontal) {
x = clientX - seriesBound.left + 15;
y = handleYForBars();
} else {
x = handleXForColumns(x);
y = e.clientY - seriesBound.top - ttCtx.tooltipRect.ttHeight / 2 - 15;
}
} else {
if (w.globals.isBarHorizontal) {
x = cx;
if (ttCtx.xyRatios && x < ttCtx.xyRatios.baseLineInvertedY) {
x = cx - ttCtx.tooltipRect.ttWidth;
}
y = handleYForBars();
} else {
x = handleXForColumns(x);
y = cy;
}
}
}
return {
x,
y,
barHeight,
barWidth,
i,
j
};
}
}
class AxesTooltip {
/**
* @param {import('./Tooltip').default} tooltipContext
*/
constructor(tooltipContext) {
this.w = tooltipContext.w;
this.ttCtx = tooltipContext;
}
/**
* This method adds the secondary tooltip which appears below x axis
* @memberof Tooltip
**/
drawXaxisTooltip() {
const w = this.w;
const ttCtx = this.ttCtx;
const isBottom = w.config.xaxis.position === "bottom";
ttCtx.xaxisOffY = isBottom ? w.layout.gridHeight + 1 : -w.layout.xAxisHeight - w.config.xaxis.axisTicks.height + 3;
const tooltipCssClass = isBottom ? "apexcharts-xaxistooltip apexcharts-xaxistooltip-bottom" : "apexcharts-xaxistooltip apexcharts-xaxistooltip-top";
const renderTo = w.dom.elWrap;
if (ttCtx.isXAxisTooltipEnabled) {
const xaxisTooltip = w.dom.baseEl.querySelector(
".apexcharts-xaxistooltip"
);
if (xaxisTooltip === null) {
ttCtx.xaxisTooltip = BrowserAPIs.createElementNS(
"http://www.w3.org/1999/xhtml",
"div"
);
ttCtx.xaxisTooltip.setAttribute(
"class",
tooltipCssClass + " apexcharts-theme-" + w.config.tooltip.theme
);
renderTo.appendChild(ttCtx.xaxisTooltip);
ttCtx.xaxisTooltipText = BrowserAPIs.createElementNS(
"http://www.w3.org/1999/xhtml",
"div"
);
ttCtx.xaxisTooltipText.classList.add("apexcharts-xaxistooltip-text");
ttCtx.xaxisTooltipText.style.fontFamily = w.config.xaxis.tooltip.style.fontFamily || w.config.chart.fontFamily;
ttCtx.xaxisTooltipText.style.fontSize = w.config.xaxis.tooltip.style.fontSize;
ttCtx.xaxisTooltip.appendChild(ttCtx.xaxisTooltipText);
}
}
}
/**
* This method adds the secondary tooltip which appears below x axis
* @memberof Tooltip
**/
drawYaxisTooltip() {
const w = this.w;
const ttCtx = this.ttCtx;
for (let i = 0; i < w.config.yaxis.length; i++) {
const isRight = w.config.yaxis[i].opposite || w.config.yaxis[i].crosshairs.opposite;
ttCtx.yaxisOffX = isRight ? w.layout.gridWidth + 1 : 1;
const tooltipCssClass = isRight ? `apexcharts-yaxistooltip apexcharts-yaxistooltip-${i} apexcharts-yaxistooltip-right` : `apexcharts-yaxistooltip apexcharts-yaxistooltip-${i} apexcharts-yaxistooltip-left`;
const renderTo = w.dom.elWrap;
const yaxisTooltip = w.dom.baseEl.querySelector(
`.apexcharts-yaxistooltip apexcharts-yaxistooltip-${i}`
);
if (yaxisTooltip === null) {
ttCtx.yaxisTooltip = BrowserAPIs.createElementNS(
"http://www.w3.org/1999/xhtml",
"div"
);
ttCtx.yaxisTooltip.setAttribute(
"class",
tooltipCssClass + " apexcharts-theme-" + w.config.tooltip.theme
);
renderTo.appendChild(ttCtx.yaxisTooltip);
if (i === 0) ttCtx.yaxisTooltipText = [];
ttCtx.yaxisTooltipText[i] = BrowserAPIs.createElementNS("http://www.w3.org/1999/xhtml", "div");
ttCtx.yaxisTooltipText[i].classList.add(
"apexcharts-yaxistooltip-text"
);
ttCtx.yaxisTooltip.appendChild(
/** @type {any} */
ttCtx.yaxisTooltipText[i]
);
}
}
}
/**
* @memberof Tooltip
**/
setXCrosshairWidth() {
var _a, _b;
const w = this.w;
const ttCtx = this.ttCtx;
const xcrosshairs = ttCtx.getElXCrosshairs();
ttCtx.xcrosshairsWidth = parseInt(w.config.xaxis.crosshairs.width, 10);
if (!w.globals.comboCharts) {
if (w.config.xaxis.crosshairs.width === "tickWidth") {
const count = w.labelData.labels.length;
ttCtx.xcrosshairsWidth = w.layout.gridWidth / count;
} else if (w.config.xaxis.crosshairs.width === "barWidth") {
const bar = w.dom.baseEl.querySelector(".apexcharts-bar-area");
if (bar !== null) {
const barWidth = parseFloat((_a = bar.getAttribute("barWidth")) != null ? _a : "0");
ttCtx.xcrosshairsWidth = barWidth;
} else {
ttCtx.xcrosshairsWidth = 1;
}
}
} else {
const bar = w.dom.baseEl.querySelector(".apexcharts-bar-area");
if (bar !== null && w.config.xaxis.crosshairs.width === "barWidth") {
const barWidth = parseFloat((_b = bar.getAttribute("barWidth")) != null ? _b : "0");
ttCtx.xcrosshairsWidth = barWidth;
} else {
if (w.config.xaxis.crosshairs.width === "tickWidth") {
const count = w.labelData.labels.length;
ttCtx.xcrosshairsWidth = w.layout.gridWidth / count;
}
}
}
if (w.globals.isBarHorizontal) {
ttCtx.xcrosshairsWidth = 0;
}
if (xcrosshairs !== null && ttCtx.xcrosshairsWidth > 0) {
xcrosshairs.setAttribute("width", String(ttCtx.xcrosshairsWidth));
}
}
handleYCrosshair() {
const w = this.w;
const ttCtx = this.ttCtx;
ttCtx.ycrosshairs = w.dom.baseEl.querySelector(".apexcharts-ycrosshairs");
ttCtx.ycrosshairsHidden = w.dom.baseEl.querySelector(
".apexcharts-ycrosshairs-hidden"
);
}
/**
* @param {number} index
* @param {number} clientY
* @param {import('../../types/internal').XYRatios} xyRatios
*/
drawYaxisTooltipText(index, clientY, xyRatios) {
const ttCtx = this.ttCtx;
const w = this.w;
const gl = w.globals;
const yAxisSeriesArr = gl.seriesYAxisMap[index];
if (ttCtx.yaxisTooltips[index] && yAxisSeriesArr.length > 0) {
const lbFormatter = w.formatters.yLabelFormatters[index];
const elGrid = ttCtx.getElGrid();
if (!elGrid) return;
const seriesBound = elGrid.getBoundingClientRect();
const seriesIndex = yAxisSeriesArr[0];
let translationsIndex = 0;
if (xyRatios.yRatio.length > 1) {
translationsIndex = seriesIndex;
}
const hoverY = (clientY - seriesBound.top) * xyRatios.yRatio[translationsIndex];
const height = gl.maxYArr[seriesIndex] - gl.minYArr[seriesIndex];
let val = gl.minYArr[seriesIndex] + (height - hoverY);
if (w.config.yaxis[index].reversed) {
val = gl.maxYArr[seriesIndex] - (height - hoverY);
}
ttCtx.tooltipPosition.moveYCrosshairs(clientY - seriesBound.top);
ttCtx.yaxisTooltipText[index].innerHTML = lbFormatter(val);
ttCtx.tooltipPosition.moveYAxisTooltip(index);
}
}
}
class Tooltip {
/**
* @param {import('../../types/internal').ChartStateW} w
* @param {import('../../types/internal').ChartContext} ctx
*/
constructor(w, ctx) {
this.w = w;
this.ctx = ctx;
this.tConfig = w.config.tooltip;
this.tooltipUtil = new Utils2(this);
this.tooltipLabels = new Labels(this);
this.tooltipPosition = new Position(this);
this.marker = new Marker(this);
this.intersect = new Intersect(this);
this.axesTooltip = new AxesTooltip(this);
this.showOnIntersect = this.tConfig.intersect;
this.showTooltipTitle = this.tConfig.x.show;
this.fixedTooltip = this.tConfig.fixed.enabled;
this.xaxisTooltip = null;
this.xaxisTooltipText = null;
this.yaxisTooltip = null;
this.yaxisTooltipText = null;
this.yaxisTTEls = null;
this.xaxisOffY = 0;
this.yaxisOffX = 0;
this.xcrosshairsWidth = 0;
this.ycrosshairs = null;
this.ycrosshairsHidden = null;
this.tooltip = null;
this.e = null;
this.isBarShared = !w.globals.isBarHorizontal && this.tConfig.shared;
this.lastHoverTime = Date.now();
this.dimensionUpdateScheduled = false;
this.xyRatios = null;
this.isXAxisTooltipEnabled = false;
this.yaxisTooltips = [];
this.allTooltipSeriesGroups = [];
this.xAxisTicksPositions = null;
this.dataPointsDividedHeight = 0;
this.dataPointsDividedWidth = 0;
this.tooltipTitle = null;
this.legendLabels = null;
this.ttItems = null;
this.seriesBound = null;
this.seriesHoverTimeout = void 0;
this.clientX = 0;
this.clientY = 0;
this.barSeriesHeight = 0;
this.tooltipRect = { x: 0, y: 0, ttWidth: 0, ttHeight: 0 };
}
setupDimensionCache() {
const w = this.w;
const tooltipEl = this.getElTooltip();
if (!tooltipEl) return;
this.updateDimensionCache();
if (typeof ResizeObserver !== "undefined" && !w.globals.resizeObserver) {
w.globals.resizeObserver = new ResizeObserver(() => {
if (!this.dimensionUpdateScheduled) {
this.dimensionUpdateScheduled = true;
requestAnimationFrame(() => {
this.updateDimensionCache();
this.dimensionUpdateScheduled = false;
});
}
});
w.globals.resizeObserver.observe(tooltipEl);
}
}
updateDimensionCache() {
const w = this.w;
const tooltipEl = this.getElTooltip();
if (!tooltipEl) return;
const rect = tooltipEl.getBoundingClientRect();
w.globals.dimensionCache.tooltip = /** @type {any} */
{
width: rect.width,
height: rect.height,
lastUpdate: Date.now()
};
}
getCachedDimensions() {
const w = this.w;
if (w.globals.dimensionCache.tooltip) {
const cache2 = (
/** @type {Record<string,any>} */
w.globals.dimensionCache.tooltip
);
const age = Date.now() - cache2.lastUpdate;
if (age < 1e3) {
return {
ttWidth: cache2.width,
ttHeight: cache2.height
};
}
}
this.updateDimensionCache();
const cache = (
/** @type {Record<string,any>} */
w.globals.dimensionCache.tooltip
);
return cache ? {
ttWidth: cache.width,
ttHeight: cache.height
} : { ttWidth: 0, ttHeight: 0 };
}
/**
* @param {{ w: import('../../types/internal').ChartStateW }} [ctx]
* @returns {HTMLElement | null}
*/
getElTooltip(ctx) {
if (!ctx) ctx = this;
if (!ctx.w.dom.baseEl) return null;
return (
/** @type {HTMLElement | null} */
ctx.w.dom.baseEl.querySelector(".apexcharts-tooltip")
);
}
getElXCrosshairs() {
return this.w.dom.baseEl.querySelector(".apexcharts-xcrosshairs");
}
getElGrid() {
return this.w.dom.baseEl.querySelector(".apexcharts-grid");
}
/**
* @param {import('../../types/internal').XYRatios} xyRatios
*/
drawTooltip(xyRatios) {
const w = this.w;
this.xyRatios = xyRatios;
this.isXAxisTooltipEnabled = w.config.xaxis.tooltip.enabled && w.globals.axisCharts;
this.yaxisTooltips = w.config.yaxis.map((y) => {
return y.show && y.tooltip.enabled && w.globals.axisCharts ? true : false;
});
this.allTooltipSeriesGroups = [];
if (!w.globals.axisCharts) {
this.showTooltipTitle = false;
}
const existingTooltip = this.getElTooltip();
if (existingTooltip == null ? void 0 : existingTooltip.parentNode) {
existingTooltip.parentNode.removeChild(existingTooltip);
}
this.tooltipTitle = null;
const tooltipEl = BrowserAPIs.createElementNS(
"http://www.w3.org/1999/xhtml",
"div"
);
tooltipEl.classList.add("apexcharts-tooltip");
if (w.config.tooltip.cssClass) {
tooltipEl.classList.add(w.config.tooltip.cssClass);
}
tooltipEl.classList.add(`apexcharts-theme-${this.tConfig.theme || "light"}`);
if (w.config.chart.accessibility.enabled && w.config.chart.accessibility.announcements.enabled) {
tooltipEl.setAttribute("role", "tooltip");
tooltipEl.setAttribute("aria-live", "polite");
tooltipEl.setAttribute("aria-atomic", "true");
tooltipEl.setAttribute("aria-hidden", "true");
}
w.dom.elWrap.appendChild(tooltipEl);
if (w.globals.axisCharts) {
this.axesTooltip.drawXaxisTooltip();
this.axesTooltip.drawYaxisTooltip();
this.axesTooltip.setXCrosshairWidth();
this.axesTooltip.handleYCrosshair();
const xAxis = new XAxis(this.w, this.ctx, void 0);
this.xAxisTicksPositions = xAxis.getXAxisTicksPositions();
}
if ((w.globals.comboCharts || this.tConfig.intersect || w.config.chart.type === "rangeBar") && !this.tConfig.shared) {
this.showOnIntersect = true;
}
if (w.config.markers.size === 0 || w.globals.markers.largestSize === 0) {
this.marker.drawDynamicPoints();
}
if (w.globals.collapsedSeries.length === w.seriesData.series.length) return;
this.dataPointsDividedHeight = w.layout.gridHeight / w.globals.dataPoints;
this.dataPointsDividedWidth = w.layout.gridWidth / w.globals.dataPoints;
if (this.showTooltipTitle) {
this.tooltipTitle = BrowserAPIs.createElementNS(
"http://www.w3.org/1999/xhtml",
"div"
);
this.tooltipTitle.classList.add("apexcharts-tooltip-title");
this.tooltipTitle.style.fontFamily = this.tConfig.style.fontFamily || w.config.chart.fontFamily;
this.tooltipTitle.style.fontSize = this.tConfig.style.fontSize;
tooltipEl.appendChild(this.tooltipTitle);
}
let ttItemsCnt = w.seriesData.series.length;
if ((w.globals.xyCharts || w.globals.comboCharts) && this.tConfig.shared) {
if (!this.showOnIntersect) {
ttItemsCnt = w.seriesData.series.length;
} else {
ttItemsCnt = 1;
}
}
this.legendLabels = w.dom.baseEl.querySelectorAll(".apexcharts-legend-text");
this.ttItems = this.createTTElements(ttItemsCnt);
this.addSVGEvents();
this.setupDimensionCache();
}
/**
* @param {number} ttItemsCnt
*/
createTTElements(ttItemsCnt) {
const w = this.w;
const ttItems = [];
const tooltipEl = this.getElTooltip();
if (!tooltipEl) return ttItems;
for (let i = 0; i < ttItemsCnt; i++) {
const gTxt = BrowserAPIs.createElementNS(
"http://www.w3.org/1999/xhtml",
"div"
);
gTxt.classList.add(
"apexcharts-tooltip-series-group",
`apexcharts-tooltip-series-group-${i}`
);
gTxt.style.order = String(
w.config.tooltip.inverseOrder ? ttItemsCnt - i : i + 1
);
const point = BrowserAPIs.createElementNS(
"http://www.w3.org/1999/xhtml",
"span"
);
point.classList.add("apexcharts-tooltip-marker");
if (w.config.tooltip.fillSeriesColor) {
point.style.backgroundColor = w.globals.colors[i];
} else {
point.style.color = w.globals.colors[i];
}
const mShape = w.config.markers.shape;
let shape = mShape;
if (Array.isArray(mShape)) {
shape = mShape[i];
}
point.setAttribute("shape", shape);
gTxt.appendChild(point);
const gYZ = BrowserAPIs.createElementNS(
"http://www.w3.org/1999/xhtml",
"div"
);
gYZ.classList.add("apexcharts-tooltip-text");
gYZ.style.fontFamily = this.tConfig.style.fontFamily || w.config.chart.fontFamily;
gYZ.style.fontSize = this.tConfig.style.fontSize;
["y", "goals", "z"].forEach((g) => {
const gValText = BrowserAPIs.createElementNS(
"http://www.w3.org/1999/xhtml",
"div"
);
gValText.classList.add(`apexcharts-tooltip-${g}-group`);
const txtLabel = BrowserAPIs.createElementNS(
"http://www.w3.org/1999/xhtml",
"span"
);
txtLabel.classList.add(`apexcharts-tooltip-text-${g}-label`);
gValText.appendChild(txtLabel);
const txtValue = BrowserAPIs.createElementNS(
"http://www.w3.org/1999/xhtml",
"span"
);
txtValue.classList.add(`apexcharts-tooltip-text-${g}-value`);
gValText.appendChild(txtValue);
gYZ.appendChild(gValText);
});
gTxt.appendChild(gYZ);
tooltipEl.appendChild(gTxt);
ttItems.push(gTxt);
}
return ttItems;
}
addSVGEvents() {
const w = this.w;
const type = w.config.chart.type;
const tooltipEl = this.getElTooltip();
if (!tooltipEl) return;
const commonBar = !!(type === "bar" || type === "candlestick" || type === "boxPlot" || type === "rangeBar");
const chartWithmarkers = type === "area" || type === "line" || type === "scatter" || type === "bubble" || type === "radar";
const hoverArea = w.dom.Paper.node;
const elGrid = this.getElGrid();
if (elGrid) {
this.seriesBound = elGrid.getBoundingClientRect();
}
const tooltipY = [];
const tooltipX = [];
const seriesHoverParams = {
hoverArea,
elGrid,
tooltipEl,
tooltipY,
tooltipX,
ttItems: this.ttItems
};
let points;
if (w.globals.axisCharts) {
if (chartWithmarkers) {
points = w.dom.baseEl.querySelectorAll(
".apexcharts-series[data\\:longestSeries='true'] .apexcharts-marker"
);
} else if (commonBar) {
points = w.dom.baseEl.querySelectorAll(
".apexcharts-series .apexcharts-bar-area, .apexcharts-series .apexcharts-candlestick-area, .apexcharts-series .apexcharts-boxPlot-area, .apexcharts-series .apexcharts-rangebar-area"
);
} else if (type === "heatmap" || type === "treemap") {
points = w.dom.baseEl.querySelectorAll(
".apexcharts-series .apexcharts-heatmap, .apexcharts-series .apexcharts-treemap"
);
}
if (points && points.length) {
for (let p = 0; p < points.length; p++) {
tooltipY.push(points[p].getAttribute("cy"));
tooltipX.push(points[p].getAttribute("cx"));
}
}
}
const validSharedChartTypes = w.globals.xyCharts && !this.showOnIntersect || w.globals.comboCharts && !this.showOnIntersect || commonBar && this.tooltipUtil.hasBars() && this.tConfig.shared;
if (validSharedChartTypes) {
this.addPathsEventListeners([hoverArea], seriesHoverParams);
} else if (commonBar && !w.globals.comboCharts || chartWithmarkers && this.showOnIntersect) {
this.addDatapointEventsListeners(seriesHoverParams);
} else if (!w.globals.axisCharts || type === "heatmap" || type === "treemap") {
const seriesAll = w.dom.baseEl.querySelectorAll(".apexcharts-series");
this.addPathsEventListeners(seriesAll, seriesHoverParams);
}
if (this.showOnIntersect) {
const lineAreaPoints = w.dom.baseEl.querySelectorAll(
".apexcharts-line-series .apexcharts-marker, .apexcharts-area-series .apexcharts-marker"
);
if (lineAreaPoints.length > 0) {
this.addPathsEventListeners(lineAreaPoints, seriesHoverParams);
}
if (this.tooltipUtil.hasBars() && !this.tConfig.shared) {
this.addDatapointEventsListeners(seriesHoverParams);
}
}
}
drawFixedTooltipRect() {
const w = this.w;
const tooltipEl = this.getElTooltip();
if (!tooltipEl) return { x: 0, y: 0, ttWidth: 0, ttHeight: 0 };
const tooltipRect = tooltipEl.getBoundingClientRect();
const ttWidth = tooltipRect.width + 10;
const ttHeight = tooltipRect.height + 10;
let x = this.tConfig.fixed.offsetX;
let y = this.tConfig.fixed.offsetY;
const fixed = this.tConfig.fixed.position.toLowerCase();
if (fixed.indexOf("right") > -1) {
x = x + w.globals.svgWidth - ttWidth + 10;
}
if (fixed.indexOf("bottom") > -1) {
y = y + w.globals.svgHeight - ttHeight - 10;
}
tooltipEl.style.left = x + "px";
tooltipEl.style.top = y + "px";
return {
x,
y,
ttWidth,
ttHeight
};
}
/**
* @param {Record<string, any>} seriesHoverParams
*/
addDatapointEventsListeners(seriesHoverParams) {
const w = this.w;
const points = w.dom.baseEl.querySelectorAll(
".apexcharts-series-markers .apexcharts-marker, .apexcharts-bar-area, .apexcharts-candlestick-area, .apexcharts-boxPlot-area, .apexcharts-rangebar-area"
);
this.addPathsEventListeners(points, seriesHoverParams);
}
/**
* @param {any} paths
* @param {Record<string, any>} opts
*/
addPathsEventListeners(paths, opts) {
const self = this;
for (let p = 0; p < paths.length; p++) {
const extendedOpts = {
paths: paths[p],
tooltipEl: opts.tooltipEl,
tooltipY: opts.tooltipY,
tooltipX: opts.tooltipX,
elGrid: opts.elGrid,
hoverArea: opts.hoverArea,
ttItems: opts.ttItems
};
const events = [
"mousemove",
"mouseup",
"touchmove",
"mouseout",
"touchend"
];
events.map((ev) => {
return paths[p].addEventListener(
ev,
self.onSeriesHover.bind(self, extendedOpts),
{ capture: false, passive: true }
);
});
}
}
/*
** Check to see if the tooltips should be updated based on a mouse / touch event
* @param {Record<string, any>} opt
* @param {Event} e
*/
/** @param {Record<string, any>} opt @param {any} e */
onSeriesHover(opt, e) {
const targetDelay = 20;
const timeSinceLastUpdate = Date.now() - this.lastHoverTime;
if (timeSinceLastUpdate >= targetDelay) {
this.seriesHover(opt, e);
} else {
clearTimeout(this.seriesHoverTimeout);
this.seriesHoverTimeout = setTimeout(() => {
this.seriesHover(opt, e);
}, targetDelay - timeSinceLastUpdate);
}
}
/*
** The actual series hover function
* @param {Record<string, any>} opt
* @param {Event} e
*/
/** @param {Record<string, any>} opt @param {any} e */
seriesHover(opt, e) {
this.lastHoverTime = Date.now();
let chartGroups = [];
const w = this.w;
if (w.config.chart.group) {
chartGroups = this.ctx.getGroupedCharts();
}
if (w.globals.axisCharts && (w.globals.minX === -Infinity && w.globals.maxX === Infinity || w.globals.dataPoints === 0)) {
return;
}
if (chartGroups.length) {
chartGroups.forEach((ch) => {
const tooltipEl = this.getElTooltip(ch);
const newOpts = {
paths: opt.paths,
tooltipEl,
tooltipY: opt.tooltipY,
tooltipX: opt.tooltipX,
elGrid: opt.elGrid,
hoverArea: opt.hoverArea,
ttItems: ch.w.globals.tooltip.ttItems
};
if (ch.w.globals.minX === this.w.globals.minX && ch.w.globals.maxX === this.w.globals.maxX) {
ch.w.globals.tooltip.seriesHoverByContext({
chartCtx: ch,
ttCtx: ch.w.globals.tooltip,
opt: newOpts,
e
});
}
});
} else {
this.seriesHoverByContext({
chartCtx: this.ctx,
ttCtx: this.w.globals.tooltip,
opt,
e
});
}
}
/** @param {{chartCtx: any, ttCtx: any, opt: any, e: any}} opts */
seriesHoverByContext({ chartCtx, ttCtx, opt, e }) {
const w = chartCtx.w;
const tooltipEl = this.getElTooltip(chartCtx);
if (!tooltipEl) return;
const cachedDims = ttCtx.getCachedDimensions();
ttCtx.tooltipRect = {
x: 0,
y: 0,
ttWidth: cachedDims.ttWidth,
ttHeight: cachedDims.ttHeight
};
ttCtx.e = e;
if (ttCtx.tooltipUtil.hasBars() && !w.globals.comboCharts && !ttCtx.isBarShared) {
if (this.tConfig.onDatasetHover.highlightDataSeries) {
const series = new Series(chartCtx.w);
series.toggleSeriesOnHover(e, e.target.parentNode);
}
}
if (w.globals.axisCharts) {
ttCtx.axisChartsTooltips({
e,
opt,
tooltipRect: ttCtx.tooltipRect
});
} else {
ttCtx.nonAxisChartsTooltips({
e,
opt,
tooltipRect: ttCtx.tooltipRect
});
}
if (ttCtx.fixedTooltip) {
ttCtx.drawFixedTooltipRect();
}
}
// tooltip handling for line/area/bar/columns/scatter
/** @param {{e: any, opt: any}} opts */
axisChartsTooltips({ e, opt }) {
var _a;
const w = this.w;
let x, y;
const seriesBound = opt.elGrid.getBoundingClientRect();
const clientX = e.type === "touchmove" ? e.touches[0].clientX : e.clientX;
const clientY = e.type === "touchmove" ? e.touches[0].clientY : e.clientY;
this.clientY = clientY;
this.clientX = clientX;
w.interact.capturedSeriesIndex = -1;
w.interact.capturedDataPointIndex = -1;
if (clientY < seriesBound.top || clientY > seriesBound.top + seriesBound.height) {
this.handleMouseOut(opt);
return;
}
if (Array.isArray(this.tConfig.enabledOnSeries) && !w.config.tooltip.shared) {
const index = parseInt(opt.paths.getAttribute("index"), 10);
if (this.tConfig.enabledOnSeries.indexOf(index) < 0) {
this.handleMouseOut(opt);
return;
}
}
const tooltipEl = this.getElTooltip();
if (!tooltipEl) return;
const xcrosshairs = this.getElXCrosshairs();
let syncedCharts = [];
if (w.config.chart.group) {
syncedCharts = this.ctx.getSyncedCharts();
}
const isStickyTooltip = w.globals.xyCharts || w.config.chart.type === "bar" && !w.globals.isBarHorizontal && this.tooltipUtil.hasBars() && this.tConfig.shared || w.globals.comboCharts && this.tooltipUtil.hasBars();
if (e.type === "mousemove" || e.type === "touchmove" || e.type === "mouseup") {
if (w.globals.collapsedSeries.length + w.globals.ancillaryCollapsedSeries.length === w.seriesData.series.length) {
return;
}
if (xcrosshairs !== null) {
xcrosshairs.classList.add("apexcharts-active");
}
const hasYAxisTooltip = (_a = this.yaxisTooltips) == null ? void 0 : _a.filter(
(b) => {
return b === true;
}
);
const _yc = (
/** @type {any} */
this.ycrosshairs
);
if (_yc !== null && (hasYAxisTooltip == null ? void 0 : hasYAxisTooltip.length)) {
_yc.classList.add("apexcharts-active");
}
if (isStickyTooltip && !this.showOnIntersect || syncedCharts.length > 1) {
this.handleStickyTooltip(e, clientX, clientY, opt);
} else {
if (w.config.chart.type === "heatmap" || w.config.chart.type === "treemap") {
const markerXY = this.intersect.handleHeatTreeTooltip({
e,
opt,
x,
y,
type: w.config.chart.type
});
x = markerXY.x;
y = markerXY.y;
tooltipEl.style.left = x + "px";
tooltipEl.style.top = y + "px";
} else {
if (this.tooltipUtil.hasBars()) {
this.intersect.handleBarTooltip({
e,
opt
});
}
if (this.tooltipUtil.hasMarkers(0)) {
this.intersect.handleMarkerTooltip({
e,
opt,
x,
y
});
}
}
}
if (this.yaxisTooltips && this.yaxisTooltips.length) {
for (let yt = 0; yt < w.config.yaxis.length; yt++) {
this.axesTooltip.drawYaxisTooltipText(
yt,
clientY,
/** @type {import('../../types/internal').XYRatios} */
this.xyRatios
);
}
}
w.dom.baseEl.classList.add("apexcharts-tooltip-active");
opt.tooltipEl.classList.add("apexcharts-active");
if (w.config.chart.accessibility.enabled && w.config.chart.accessibility.announcements.enabled) {
opt.tooltipEl.removeAttribute("aria-hidden");
}
} else if (e.type === "mouseout" || e.type === "touchend") {
this.handleMouseOut(opt);
}
}
// tooltip handling for pie/donuts
/** @param {{e: any, opt: any, tooltipRect: any}} opts */
nonAxisChartsTooltips({ e, opt, tooltipRect }) {
var _a, _b, _c, _d;
const w = this.w;
const rel = opt.paths.getAttribute("rel");
const tooltipEl = this.getElTooltip();
if (!tooltipEl) return;
const seriesBound = w.dom.elWrap.getBoundingClientRect();
if (e.type === "mousemove" || e.type === "touchmove") {
w.dom.baseEl.classList.add("apexcharts-tooltip-active");
tooltipEl.classList.add("apexcharts-active");
if (w.config.chart.accessibility.enabled && w.config.chart.accessibility.announcements.enabled) {
tooltipEl.removeAttribute("aria-hidden");
}
this.tooltipLabels.drawSeriesTexts({
ttItems: opt.ttItems,
i: parseInt(rel, 10) - 1,
shared: false
});
let x, y;
const arcPath = opt.paths.querySelector("path[data\\:cx]") || opt.paths;
if (w.config.tooltip.intersect && arcPath.hasAttribute("data:cx") && arcPath.hasAttribute("data:cy")) {
const svgBound = w.dom.Paper.node.getBoundingClientRect();
x = svgBound.left - seriesBound.left + parseFloat(arcPath.getAttribute("data:cx")) - tooltipRect.ttWidth / 2;
y = svgBound.top - seriesBound.top + parseFloat(arcPath.getAttribute("data:cy")) - tooltipRect.ttHeight - 10;
} else {
x = ((_a = w.interact.clientX) != null ? _a : 0) - seriesBound.left - tooltipRect.ttWidth / 2;
y = ((_b = w.interact.clientY) != null ? _b : 0) - seriesBound.top - tooltipRect.ttHeight - 10;
}
tooltipEl.style.left = x + "px";
tooltipEl.style.top = y + "px";
if (w.config.legend.tooltipHoverFormatter) {
const legendFormatter = w.config.legend.tooltipHoverFormatter;
const i = rel - 1;
const legendEl = (
/** @type {HTMLElement | undefined} */
(_c = this.legendLabels) == null ? void 0 : _c[i]
);
if (!legendEl) return;
const legendName = legendEl.getAttribute("data:default-text");
const text = legendFormatter(legendName, {
seriesIndex: i,
dataPointIndex: i,
w
});
legendEl.innerHTML = text;
}
} else if (e.type === "mouseout" || e.type === "touchend") {
tooltipEl.classList.remove("apexcharts-active");
w.dom.baseEl.classList.remove("apexcharts-tooltip-active");
if (w.config.legend.tooltipHoverFormatter) {
(_d = this.legendLabels) == null ? void 0 : _d.forEach((l) => {
const defaultText = l.getAttribute("data:default-text");
l.innerHTML = decodeURIComponent(
defaultText != null ? defaultText : ""
);
});
}
}
}
/**
* @param {Event} e
* @param {number} clientX
* @param {number} clientY
* @param {Record<string, any>} opt
*/
handleStickyTooltip(e, clientX, clientY, opt) {
const w = this.w;
const capj = this.tooltipUtil.getNearestValues({
context: this,
hoverArea: opt.hoverArea,
elGrid: opt.elGrid,
clientX,
clientY
});
const j = capj.j;
let capturedSeries = capj.capturedSeries;
if (capturedSeries !== null && w.globals.collapsedSeriesIndices.includes(capturedSeries != null ? capturedSeries : -1))
capturedSeries = null;
const bounds = opt.elGrid.getBoundingClientRect();
if (capj.hoverX < 0 || capj.hoverX > bounds.width) {
this.handleMouseOut(opt);
return;
}
if (capturedSeries !== null) {
this.handleStickyCapturedSeries(e, capturedSeries != null ? capturedSeries : -1, opt, j != null ? j : 0);
} else {
if (this.tooltipUtil.isXoverlap(j != null ? j : 0) || w.globals.isBarHorizontal) {
const firstVisibleSeries = w.seriesData.series.findIndex(
/**
* @param {any} s
* @param {number} i
*/
(s, i) => !w.globals.collapsedSeriesIndices.includes(i)
);
this.create(e, this, firstVisibleSeries, j != null ? j : 0, opt.ttItems);
}
}
}
/**
* @param {Event} e
* @param {number} capturedSeries
* @param {Record<string, any>} opt
* @param {number} j
*/
handleStickyCapturedSeries(e, capturedSeries, opt, j) {
const w = this.w;
if (!this.tConfig.shared) {
const ignoreNull = w.seriesData.series[capturedSeries][j] === null;
if (ignoreNull) {
this.handleMouseOut(opt);
return;
}
}
if (typeof w.seriesData.series[capturedSeries][j] !== "undefined") {
if (this.tConfig.shared && this.tooltipUtil.isXoverlap(j) && this.tooltipUtil.isInitialSeriesSameLen()) {
this.create(e, this, capturedSeries, j, opt.ttItems);
} else {
this.create(e, this, capturedSeries, j, opt.ttItems, false);
}
} else {
if (this.tooltipUtil.isXoverlap(j)) {
const firstVisibleSeries = w.seriesData.series.findIndex(
/**
* @param {any} s
* @param {number} i
*/
(s, i) => !w.globals.collapsedSeriesIndices.includes(i)
);
this.create(e, this, firstVisibleSeries, j, opt.ttItems);
}
}
}
deactivateHoverFilter() {
const w = this.w;
const graphics = new Graphics(this.w, this.ctx);
const allPaths = w.dom.Paper.find(`.apexcharts-bar-area`);
for (let b = 0; b < allPaths.length; b++) {
graphics.pathMouseLeave(
/** @type {any} */
allPaths[b],
/** @type {any} */
void 0
);
}
}
/**
* @param {Record<string, any>} opt
*/
handleMouseOut(opt) {
var _a, _b;
const w = this.w;
const xcrosshairs = this.getElXCrosshairs();
w.dom.baseEl.classList.remove("apexcharts-tooltip-active");
opt.tooltipEl.classList.remove("apexcharts-active");
if (w.config.chart.accessibility.enabled && w.config.chart.accessibility.announcements.enabled) {
opt.tooltipEl.setAttribute("aria-hidden", "true");
}
this.deactivateHoverFilter();
if (w.config.chart.type !== "bubble") {
this.marker.resetPointsSize();
}
if (xcrosshairs !== null) {
xcrosshairs.classList.remove("apexcharts-active");
}
const _yc2 = (
/** @type {any} */
this.ycrosshairs
);
if (_yc2 !== null) {
_yc2.classList.remove("apexcharts-active");
}
if (this.isXAxisTooltipEnabled) {
(_a = this.xaxisTooltip) == null ? void 0 : _a.classList.remove("apexcharts-active");
}
if (this.yaxisTooltips && this.yaxisTooltips.length) {
if (this.yaxisTTEls === null) {
this.yaxisTTEls = /** @type {HTMLElement[]} */
[
...w.dom.baseEl.querySelectorAll(".apexcharts-yaxistooltip")
];
}
for (let i = 0; i < this.yaxisTTEls.length; i++) {
this.yaxisTTEls[i].classList.remove("apexcharts-active");
}
}
if (w.config.legend.tooltipHoverFormatter) {
(_b = this.legendLabels) == null ? void 0 : _b.forEach((l) => {
const defaultText = l.getAttribute("data:default-text");
l.innerHTML = decodeURIComponent(
defaultText != null ? defaultText : ""
);
});
}
}
/**
* @param {Event} e
* @param {number} seriesIndex
* @param {number} dataPointIndex
*/
markerClick(e, seriesIndex, dataPointIndex) {
const w = this.w;
if (typeof w.config.chart.events.markerClick === "function") {
w.config.chart.events.markerClick(e, this.ctx, {
seriesIndex,
dataPointIndex,
w
});
}
this.ctx.events.fireEvent("markerClick", [
e,
this.ctx,
{ seriesIndex, dataPointIndex, w }
]);
}
/**
* @param {Event} e
* @param {any} context
* @param {number} capturedSeries
* @param {number} j
* @param {any} ttItems
* @param {boolean | null} shared
*/
create(e, context, capturedSeries, j, ttItems, shared = null) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s;
const w = this.w;
const ttCtx = context;
if (e.type === "mouseup") {
this.markerClick(e, capturedSeries, j);
}
if (shared === null) shared = this.tConfig.shared;
const hasMarkers = this.tooltipUtil.hasMarkers(capturedSeries);
const bars = this.tooltipUtil.getElBars();
const handlePoints = () => {
if (w.globals.markers.largestSize > 0) {
ttCtx.marker.enlargePoints(j);
} else {
ttCtx.tooltipPosition.moveDynamicPointsOnHover(j);
}
};
if (w.config.legend.tooltipHoverFormatter) {
const legendFormatter = w.config.legend.tooltipHoverFormatter;
const els = (
/** @type {HTMLElement[]} */
Array.from((_a = this.legendLabels) != null ? _a : [])
);
els.forEach((l) => {
const legendName = l.getAttribute("data:default-text");
l.innerHTML = decodeURIComponent(legendName != null ? legendName : "");
});
for (let i = 0; i < els.length; i++) {
const l = els[i];
const lsIndex = parseInt((_b = l.getAttribute("i")) != null ? _b : "", 10);
const legendName = decodeURIComponent(
(_c = l.getAttribute("data:default-text")) != null ? _c : ""
);
const text = legendFormatter(legendName, {
seriesIndex: shared ? lsIndex : capturedSeries,
dataPointIndex: j,
w
});
if (!shared) {
l.innerHTML = lsIndex === capturedSeries ? text : legendName;
if (capturedSeries === lsIndex) {
break;
}
} else {
l.innerHTML = w.globals.collapsedSeriesIndices.indexOf(lsIndex) < 0 ? text : legendName;
}
}
}
const _rangeData = (
/** @type {any} */
w.rangeData
);
const commonSeriesTextsParams = __spreadValues(__spreadValues({
ttItems,
i: capturedSeries,
j
}, ((_g = (_f = (_e = (_d = _rangeData.seriesRange) == null ? void 0 : _d[capturedSeries]) == null ? void 0 : _e[j]) == null ? void 0 : _f.y[0]) == null ? void 0 : _g.y1) !== void 0 && {
y1: (_k = (_j = (_i = (_h = _rangeData.seriesRange) == null ? void 0 : _h[capturedSeries]) == null ? void 0 : _i[j]) == null ? void 0 : _j.y[0]) == null ? void 0 : _k.y1
}), ((_o = (_n = (_m = (_l = _rangeData.seriesRange) == null ? void 0 : _l[capturedSeries]) == null ? void 0 : _m[j]) == null ? void 0 : _n.y[0]) == null ? void 0 : _o.y2) !== void 0 && {
y2: (_s = (_r = (_q = (_p = _rangeData.seriesRange) == null ? void 0 : _p[capturedSeries]) == null ? void 0 : _q[j]) == null ? void 0 : _r.y[0]) == null ? void 0 : _s.y2
});
if (shared) {
ttCtx.tooltipLabels.drawSeriesTexts(__spreadProps(__spreadValues({}, commonSeriesTextsParams), {
shared: this.showOnIntersect ? false : this.tConfig.shared
}));
if (hasMarkers) {
handlePoints();
} else if (this.tooltipUtil.hasBars()) {
this.barSeriesHeight = this.tooltipUtil.getBarsHeight(
/** @type {any[]} */
[...bars]
);
if (this.barSeriesHeight > 0) {
const graphics = new Graphics(this.w, this.ctx);
const paths = w.dom.Paper.find(`.apexcharts-bar-area[j='${j}']`);
this.deactivateHoverFilter();
const points = ttCtx.tooltipUtil.getAllMarkers(true);
if (points.length && !this.barSeriesHeight) {
handlePoints();
}
ttCtx.tooltipPosition.moveStickyTooltipOverBars(j, capturedSeries);
for (let b = 0; b < paths.length; b++) {
graphics.pathMouseEnter(
/** @type {any} */
paths[b],
/** @type {any} */
void 0
);
}
}
}
} else {
ttCtx.tooltipLabels.drawSeriesTexts(__spreadValues({
shared: false
}, commonSeriesTextsParams));
if (this.tooltipUtil.hasBars()) {
ttCtx.tooltipPosition.moveStickyTooltipOverBars(j, capturedSeries);
}
if (hasMarkers) {
ttCtx.tooltipPosition.moveMarkers(capturedSeries, j);
}
}
}
}
let SVGElement$1 = class SVGElement2 {
/**
* @param {any} node
*/
constructor(node) {
this.node = node;
if (node) {
node.instance = this;
}
this._listeners = [];
this._filter = null;
}
// ---- Attribute methods ----
/**
* @param {any} a
* @param {any} [v]
*/
attr(a, v) {
if (typeof a === "string" && v === void 0) {
return this.node.getAttribute(a);
}
const attrs = typeof a === "string" ? { [a]: v } : a;
for (const key in attrs) {
let val = attrs[key];
if (val === null) {
this.node.removeAttribute(key);
} else if (val !== void 0) {
if (typeof val === "number" && isNaN(val)) val = 0;
this.node.setAttribute(key, val);
}
}
if (this.node.nodeName === "text" && attrs.x != null) {
const tspans = this.node.querySelectorAll("tspan[data-newline]");
for (let i = 0; i < tspans.length; i++) {
tspans[i].setAttribute("x", attrs.x);
}
}
return this;
}
/**
* @param {Record<string, string>} styles
*/
css(styles) {
for (const k in styles) {
this.node.style[k] = styles[k];
}
return this;
}
/**
* @param {any} v
*/
fill(v) {
if (typeof v === "object") {
return this.attr(v);
}
return this.attr("fill", v);
}
/**
* @param {any} v
*/
stroke(v) {
if (typeof v === "object") {
if (v.color !== void 0) this.attr("stroke", v.color);
if (v.width !== void 0) this.attr("stroke-width", v.width);
if (v.dasharray !== void 0) this.attr("stroke-dasharray", v.dasharray);
if (v.linecap !== void 0) this.attr("stroke-linecap", v.linecap);
if (v.opacity !== void 0) this.attr("stroke-opacity", v.opacity);
return this;
}
return this.attr("stroke", v);
}
/**
* @param {number} w
* @param {number} h
*/
size(w, h) {
return this.attr({ width: w, height: h });
}
/**
* @param {number} x
* @param {number} y
*/
move(x, y) {
return this.attr({ x, y });
}
/**
* @param {number} cx
* @param {number} cy
*/
center(cx, cy) {
if (this.node.nodeName === "g") {
const box = this.bbox();
const dx = cx - (box.x + box.width / 2);
const dy = cy - (box.y + box.height / 2);
return this.attr("transform", `translate(${dx}, ${dy})`);
}
return this.attr({ cx, cy });
}
// ---- Tree operations ----
/**
* @param {any} child
*/
add(child) {
this.node.appendChild(child.node || child);
return this;
}
/**
* @param {any} parent
*/
addTo(parent) {
const p = parent.node || parent;
p.appendChild(this.node);
return this;
}
remove() {
if (this.node.parentNode) {
this.node.parentNode.removeChild(this.node);
}
return this;
}
clear() {
while (this.node.firstChild) {
this.node.removeChild(this.node.firstChild);
}
return this;
}
// ---- Query ----
/**
* @param {string} selector
*/
find(selector) {
return Array.from(this.node.querySelectorAll(selector)).map(
(n) => n.instance || new SVGElement2(n)
);
}
/**
* @param {string} selector
*/
findOne(selector) {
const n = this.node.querySelector(selector);
return n ? n.instance || new SVGElement2(n) : null;
}
// ---- Events ----
/**
* @param {Event} event
* @param {Function} handler
*/
on(event, handler) {
const eventType = (
/** @type {string} */
/** @type {any} */
event.split(
"."
)[0]
);
this._listeners.push({ event, eventType, handler });
this.node.addEventListener(eventType, handler);
return this;
}
/**
* @param {Event} event
* @param {Function} handler
*/
off(event, handler) {
if (!event && !handler) {
this._listeners.forEach((l) => {
this.node.removeEventListener(l.eventType, l.handler);
});
this._listeners = [];
} else if (event && !handler) {
const eventType = (
/** @type {string} */
/** @type {any} */
event.split(".")[0]
);
this._listeners = this._listeners.filter((l) => {
if (l.eventType === eventType) {
this.node.removeEventListener(l.eventType, l.handler);
return false;
}
return true;
});
} else {
const eventType = (
/** @type {string} */
/** @type {any} */
event.split(".")[0]
);
this._listeners = this._listeners.filter((l) => {
if (l.eventType === eventType && l.handler === handler) {
this.node.removeEventListener(l.eventType, l.handler);
return false;
}
return true;
});
}
return this;
}
// ---- Iteration ----
/**
* @param {Function} fn
* @param {boolean} deep
*/
each(fn, deep) {
const children = Array.from(this.node.children);
children.forEach((child) => {
const inst = child.instance || new SVGElement2(child);
fn.call(inst);
if (deep) inst.each(fn, deep);
});
return this;
}
// ---- CSS classes ----
/**
* @param {string} cls
*/
removeClass(cls) {
if (cls === "*") {
this.node.removeAttribute("class");
} else {
this.node.classList.remove(cls);
}
return this;
}
// ---- Children ----
children() {
return Array.from(this.node.childNodes).filter((n) => n.nodeType === 1).map((n) => n.instance || new SVGElement2(n));
}
// ---- Visibility ----
hide() {
this.node.style.display = "none";
return this;
}
show() {
this.node.style.display = "";
return this;
}
// ---- Measurement ----
bbox() {
if (typeof this.node.getBBox === "function") {
try {
return this.node.getBBox();
} catch (e) {
}
}
return { x: 0, y: 0, width: 0, height: 0 };
}
// ---- Text-specific ----
/**
* @param {string} text
*/
tspan(text) {
const tspan = BrowserAPIs.createElementNS(
"http://www.w3.org/2000/svg",
"tspan"
);
tspan.textContent = text;
this.node.appendChild(tspan);
return new SVGElement2(tspan);
}
// ---- Path-specific ----
/**
* @param {string} d
*/
plot(d) {
if (typeof d === "string") {
this.attr("d", d);
}
return this;
}
// ---- Animation (overridden by SVGAnimation mixin) ----
animate() {
throw new Error("Animation module not loaded");
}
// ---- Filter methods (set up by SVGFilter module) ----
filterWith() {
throw new Error("Filter module not loaded");
}
/**
* @param {boolean} all
*/
unfilter(all) {
if (this._filter) {
this.node.removeAttribute("filter");
if (all && this._filter.node && this._filter.node.parentNode) {
this._filter.node.parentNode.removeChild(this._filter.node);
}
this._filter = null;
}
return this;
}
filterer() {
return this._filter;
}
};
let gradientCounter = 0;
class SVGGradient extends SVGElement$1 {
/**
* @param {any} container
* @param {string} type
* @param {object} builder
*/
constructor(container, type, builder) {
const tag = type === "radial" ? "radialGradient" : "linearGradient";
const node = BrowserAPIs.createElementNS(SVGNS, tag);
super(node);
this._id = "SvgjsGradient" + ++gradientCounter;
this.attr("id", this._id);
if (typeof builder === "function") {
builder(new StopBuilder(this));
}
let defs = container.node.querySelector("defs");
if (!defs) {
defs = BrowserAPIs.createElementNS(SVGNS, "defs");
container.node.appendChild(defs);
}
defs.appendChild(this.node);
}
/**
* @param {any} offset
* @param {string} color
* @param {number} opacity
*/
stop(offset, color, opacity) {
const s = BrowserAPIs.createElementNS(SVGNS, "stop");
s.setAttribute("offset", offset);
s.setAttribute("stop-color", color);
if (opacity !== void 0) s.setAttribute("stop-opacity", String(opacity));
this.node.appendChild(s);
return this;
}
/**
* @param {number} x
* @param {number} y
*/
from(x, y) {
return this.attr({ x1: x, y1: y });
}
/**
* @param {number} x
* @param {number} y
*/
to(x, y) {
return this.attr({ x2: x, y2: y });
}
url() {
return "url(#" + this._id + ")";
}
toString() {
return this.url();
}
valueOf() {
return this.url();
}
fill() {
return this.url();
}
}
class StopBuilder {
/**
* @param {any} gradient
*/
constructor(gradient) {
this.gradient = gradient;
}
/**
* @param {any} offset
* @param {string} color
* @param {number} opacity
*/
stop(offset, color, opacity) {
this.gradient.stop(offset, color, opacity);
return this;
}
}
let patternCounter = 0;
class SVGPattern extends SVGElement$1 {
/**
* @param {any} container
* @param {number} w
* @param {number} h
* @param {Function} builder
*/
constructor(container, w, h, builder) {
const node = BrowserAPIs.createElementNS(SVGNS, "pattern");
super(node);
this._id = "SvgjsPattern" + ++patternCounter;
this.attr({
id: this._id,
width: w,
height: h,
patternUnits: "userSpaceOnUse"
});
if (typeof builder === "function") {
const patternContainer = new SVGContainer(this.node);
builder(patternContainer);
}
let defs = container.node.querySelector("defs");
if (!defs) {
defs = BrowserAPIs.createElementNS(SVGNS, "defs");
container.node.appendChild(defs);
}
defs.appendChild(this.node);
}
url() {
return "url(#" + this._id + ")";
}
toString() {
return this.url();
}
valueOf() {
return this.url();
}
fill() {
return this.url();
}
}
class SVGContainer extends SVGElement$1 {
/**
* @param {number} x1
* @param {number} y1
* @param {number} x2
* @param {number} y2
*/
line(x1, y1, x2, y2) {
const el = this._make("line");
if (x1 !== void 0) {
el.attr({ x1, y1, x2, y2 });
}
return el;
}
/**
* @param {import('../types/internal').ChartStateW} w
* @param {number} h
*/
rect(w, h) {
const el = this._make("rect");
if (w !== void 0) {
el.attr({ width: w, height: h });
}
return el;
}
/**
* @param {number} d
*/
circle(d) {
const el = this._make("circle");
if (d !== void 0) {
el.attr({ r: d / 2, cx: d / 2, cy: d / 2 });
}
return el;
}
/**
* @param {string} d
*/
path(d) {
const el = this._make("path");
if (d) el.attr("d", d);
return el;
}
/**
* @param {string} pts
*/
polygon(pts) {
const el = this._make("polygon");
if (pts) el.attr("points", pts);
return el;
}
group() {
return this._makeContainer("g");
}
defs() {
return this._makeContainer("defs");
}
/**
* @param {string} textContent
*/
plain(textContent) {
const node = BrowserAPIs.createElementNS(SVGNS, "text");
node.textContent = textContent;
const el = new SVGElement$1(node);
this.node.appendChild(node);
return el;
}
/**
* @param {object} builder
*/
text(builder) {
const node = BrowserAPIs.createElementNS(SVGNS, "text");
const el = new SVGElement$1(node);
this.node.appendChild(node);
if (typeof builder === "function") {
builder(new TspanBuilder(node));
}
return el;
}
/**
* @param {string} url
* @param {Function} callback
*/
image(url, callback) {
const node = BrowserAPIs.createElementNS(SVGNS, "image");
node.setAttributeNS("http://www.w3.org/1999/xlink", "href", url);
const el = new SVGElement$1(node);
this.node.appendChild(node);
if (typeof callback === "function") {
const img = new Image();
img.onload = function() {
el.size(img.width, img.height);
callback.call(el, { width: img.width, height: img.height });
};
img.src = url;
}
return el;
}
/**
* @param {string} type
* @param {object} builder
*/
gradient(type, builder) {
return new SVGGradient(this, type, builder);
}
/**
* @param {number} w
* @param {number} h
* @param {Function} builder
*/
pattern(w, h, builder) {
return new SVGPattern(this, w, h, builder);
}
/**
* @param {string} tag
*/
_make(tag) {
const node = BrowserAPIs.createElementNS(SVGNS, tag);
this.node.appendChild(node);
return new SVGElement$1(node);
}
/**
* @param {string} tag
*/
_makeContainer(tag) {
const node = BrowserAPIs.createElementNS(SVGNS, tag);
this.node.appendChild(node);
return new SVGContainer(node);
}
}
class TspanBuilder {
/**
* @param {any} textNode
*/
constructor(textNode) {
this.textNode = textNode;
}
/**
* @param {string} text
*/
tspan(text) {
const tspan = BrowserAPIs.createElementNS(SVGNS, "tspan");
tspan.textContent = text;
this.textNode.appendChild(tspan);
return new TspanWrapper(tspan, this.textNode);
}
}
class TspanWrapper {
/**
* @param {any} node
* @param {any} textNode
*/
constructor(node, textNode) {
this.node = node;
this.textNode = textNode;
}
newLine() {
this.node.setAttribute("dy", "1.1em");
this.node.dataset.newline = "1";
return this;
}
}
let filterCounter = 0;
class SVGFilter extends SVGElement$1 {
constructor() {
const node = BrowserAPIs.createElementNS(SVGNS, "filter");
super(node);
this._id = "SvgjsFilter" + ++filterCounter;
this.attr("id", this._id);
}
/**
* @param {import('../types/internal').ChartStateW} w
* @param {number} h
* @param {number} x
* @param {number} y
*/
/**
* @param {number} w
* @param {number} h
* @param {number} [x]
* @param {number} [y]
*/
size(w, h, x, y) {
return this.attr({ width: w, height: h, x, y });
}
}
class FilterBuilder {
/**
* @param {any} filter
*/
constructor(filter) {
this.filter = filter;
}
/**
* @param {object} attrs
*/
colorMatrix(attrs) {
return this._primitive("feColorMatrix", attrs);
}
/**
* @param {object} attrs
*/
offset(attrs) {
return this._primitive("feOffset", attrs);
}
/**
* @param {object} attrs
*/
gaussianBlur(attrs) {
return this._primitive("feGaussianBlur", attrs);
}
/**
* @param {object} attrs
*/
flood(attrs) {
return this._primitive("feFlood", attrs);
}
/**
* @param {object} attrs
*/
composite(attrs) {
return this._primitive("feComposite", attrs);
}
/**
* @param {string[]} sources
*/
merge(sources) {
const m = BrowserAPIs.createElementNS(SVGNS, "feMerge");
sources.forEach((src) => {
const mn = BrowserAPIs.createElementNS(SVGNS, "feMergeNode");
mn.setAttribute("in", src);
m.appendChild(mn);
});
this.filter.node.appendChild(m);
return new SVGElement$1(m);
}
/**
* @param {string} tag
* @param {Record<string, any>} attrs
*/
_primitive(tag, attrs) {
const el = BrowserAPIs.createElementNS(SVGNS, tag);
for (const key in attrs) {
el.setAttribute(key, attrs[key]);
}
this.filter.node.appendChild(el);
return new SVGElement$1(el);
}
}
function installFilterMethods(ElementClass) {
ElementClass.prototype.filterWith = function(fn) {
const filter = new SVGFilter();
this._filter = filter;
let svgRoot = this.node;
while (svgRoot && svgRoot.nodeName !== "svg") {
svgRoot = svgRoot.parentNode;
}
if (svgRoot) {
let defs = svgRoot.querySelector("defs");
if (!defs) {
defs = BrowserAPIs.createElementNS(SVGNS, "defs");
svgRoot.insertBefore(defs, svgRoot.firstChild);
}
defs.appendChild(filter.node);
}
fn(new FilterBuilder(filter));
this.attr("filter", "url(#" + filter._id + ")");
return this;
};
ElementClass.prototype.unfilter = function(all) {
if (this._filter) {
this.node.removeAttribute("filter");
if (all && this._filter.node && this._filter.node.parentNode) {
this._filter.node.parentNode.removeChild(this._filter.node);
}
this._filter = null;
}
return this;
};
ElementClass.prototype.filterer = function() {
return this._filter;
};
}
/*!
* Path morphing for SVG path animations
* Based on svg.pathmorphing.js by Ulrich-Matthias Schäfer (MIT License)
* Refactored to be standalone (no SVG.js dependency)
*/
function parsePath(d) {
if (!d || typeof d !== "string") return [["M", 0, 0]];
const commands = [];
const re = /([MmLlHhVvCcSsQqTtAaZz])\s*/g;
const numRe = /[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?/gi;
let match;
const letters = [];
const positions = [];
while ((match = re.exec(d)) !== null) {
letters.push(match[1]);
positions.push(match.index);
}
for (let i = 0; i < letters.length; i++) {
const start = positions[i] + letters[i].length;
const end = i + 1 < positions.length ? positions[i + 1] : d.length;
const paramStr = d.substring(start, end);
const nums = [];
let numMatch;
numRe.lastIndex = 0;
while ((numMatch = numRe.exec(paramStr)) !== null) {
nums.push(parseFloat(numMatch[0]));
}
const cmd = letters[i].toUpperCase();
if (cmd === "Z") {
commands.push(["Z"]);
} else if (cmd === "M" || cmd === "L" || cmd === "T") {
for (let j = 0; j < nums.length; j += 2) {
commands.push([cmd, nums[j], nums[j + 1]]);
}
} else if (cmd === "H") {
for (let j = 0; j < nums.length; j++) {
commands.push([cmd, nums[j]]);
}
} else if (cmd === "V") {
for (let j = 0; j < nums.length; j++) {
commands.push([cmd, nums[j]]);
}
} else if (cmd === "C") {
for (let j = 0; j < nums.length; j += 6) {
commands.push([
cmd,
nums[j],
nums[j + 1],
nums[j + 2],
nums[j + 3],
nums[j + 4],
nums[j + 5]
]);
}
} else if (cmd === "S" || cmd === "Q") {
for (let j = 0; j < nums.length; j += 4) {
commands.push([cmd, nums[j], nums[j + 1], nums[j + 2], nums[j + 3]]);
}
} else if (cmd === "A") {
for (let j = 0; j < nums.length; j += 7) {
commands.push([
cmd,
nums[j],
nums[j + 1],
nums[j + 2],
nums[j + 3],
nums[j + 4],
nums[j + 5],
nums[j + 6]
]);
}
}
}
if (commands.length === 0) {
commands.push(["M", 0, 0]);
}
return commands;
}
function pathBbox(arr) {
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
arr.forEach((cmd) => {
for (let i = 1; i < cmd.length; i += 2) {
if (i + 1 <= cmd.length) {
const x = cmd[i];
const y = cmd[i + 1];
if (typeof x === "number" && typeof y === "number") {
if (x < minX) minX = x;
if (x > maxX) maxX = x;
if (y < minY) minY = y;
if (y > maxY) maxY = y;
}
}
}
});
if (minX === Infinity) {
return { x: 0, y: 0, width: 0, height: 0 };
}
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
}
function arrayToPath(arr) {
return arr.map((cmd) => cmd.join(" ")).join(" ");
}
function simplify(val) {
switch (val[0]) {
case "z":
case "Z":
val[0] = "L";
val[1] = this.start[0];
val[2] = this.start[1];
break;
case "H":
val[0] = "L";
val[2] = this.pos[1];
break;
case "V":
val[0] = "L";
val[2] = val[1];
val[1] = this.pos[0];
break;
case "T":
val[0] = "Q";
val[3] = val[1];
val[4] = val[2];
val[1] = this.reflection[1];
val[2] = this.reflection[0];
break;
case "S":
val[0] = "C";
val[6] = val[4];
val[5] = val[3];
val[4] = val[2];
val[3] = val[1];
val[2] = this.reflection[1];
val[1] = this.reflection[0];
break;
}
return val;
}
function setPosAndReflection(val) {
var len = val.length;
this.pos = [val[len - 2], val[len - 1]];
if ("SCQT".indexOf(val[0]) != -1) {
this.reflection = [
2 * this.pos[0] - val[len - 4],
2 * this.pos[1] - val[len - 3]
];
}
return val;
}
function toBezier(val) {
var _a;
var retVal = [val];
switch (val[0]) {
case "M":
this.pos = this.start = [val[1], val[2]];
return retVal;
case "L":
val[5] = val[3] = val[1];
val[6] = val[4] = val[2];
val[1] = this.pos[0];
val[2] = this.pos[1];
break;
case "Q":
val[6] = val[4];
val[5] = val[3];
val[4] = val[4] * 1 / 3 + val[2] * 2 / 3;
val[3] = val[3] * 1 / 3 + val[1] * 2 / 3;
val[2] = this.pos[1] * 1 / 3 + val[2] * 2 / 3;
val[1] = this.pos[0] * 1 / 3 + val[1] * 2 / 3;
break;
case "A":
retVal = arcToBezier((_a = this.pos) != null ? _a : [], val);
val = retVal[0];
break;
}
val[0] = "C";
this.pos = [val[5], val[6]];
this.reflection = [2 * val[5] - val[3], 2 * val[6] - val[4]];
return retVal;
}
function findNextM(arr, offset) {
if (offset === false) return false;
for (var i = offset, len = arr.length; i < len; ++i) {
if (arr[i][0] == "M") return i;
}
return false;
}
function arcToBezier(pos, val) {
var rx = Math.abs(val[1]), ry = Math.abs(val[2]), xAxisRotation = val[3] % 360, largeArcFlag = val[4], sweepFlag = val[5], x = val[6], y = val[7], A = new Point(pos[0], pos[1]), B = new Point(x, y), primedCoord, lambda, mat, k, c, cSquare, t, O, OA, OB, tetaStart, tetaEnd, deltaTeta, nbSectors, f, arcSegPoints, angle, sinAngle, cosAngle, pt, i, il, retVal = [], x1, y1, x2, y2;
if (rx === 0 || ry === 0 || A.x === B.x && A.y === B.y) {
return [["C", A.x, A.y, B.x, B.y, B.x, B.y]];
}
primedCoord = new Point((A.x - B.x) / 2, (A.y - B.y) / 2).transform(
/** @type {any} */
new Matrix(0, 0, 0, 0, 0, 0).rotate(xAxisRotation)
);
lambda = primedCoord.x * primedCoord.x / (rx * rx) + primedCoord.y * primedCoord.y / (ry * ry);
if (lambda > 1) {
lambda = Math.sqrt(lambda);
rx = lambda * rx;
ry = lambda * ry;
}
mat = /** @type {any} */
new Matrix(0, 0, 0, 0, 0, 0).rotate(xAxisRotation).scale(1 / rx, 1 / ry).rotate(-xAxisRotation);
A = A.transform(mat);
B = B.transform(mat);
k = [B.x - A.x, B.y - A.y];
cSquare = k[0] * k[0] + k[1] * k[1];
c = Math.sqrt(cSquare);
k[0] /= c;
k[1] /= c;
t = cSquare < 4 ? Math.sqrt(1 - cSquare / 4) : 0;
if (largeArcFlag === sweepFlag) {
t *= -1;
}
O = new Point((B.x + A.x) / 2 + t * -k[1], (B.y + A.y) / 2 + t * k[0]);
OA = new Point(A.x - O.x, A.y - O.y);
OB = new Point(B.x - O.x, B.y - O.y);
tetaStart = Math.acos(OA.x / Math.sqrt(OA.x * OA.x + OA.y * OA.y));
if (OA.y < 0) tetaStart *= -1;
tetaEnd = Math.acos(OB.x / Math.sqrt(OB.x * OB.x + OB.y * OB.y));
if (OB.y < 0) tetaEnd *= -1;
if (sweepFlag && tetaStart > tetaEnd) {
tetaEnd += 2 * Math.PI;
}
if (!sweepFlag && tetaStart < tetaEnd) {
tetaEnd -= 2 * Math.PI;
}
nbSectors = Math.ceil(Math.abs(tetaStart - tetaEnd) * 2 / Math.PI);
arcSegPoints = [];
angle = tetaStart;
deltaTeta = (tetaEnd - tetaStart) / nbSectors;
f = 4 * Math.tan(deltaTeta / 4) / 3;
for (i = 0; i <= nbSectors; i++) {
cosAngle = Math.cos(angle);
sinAngle = Math.sin(angle);
pt = new Point(O.x + cosAngle, O.y + sinAngle);
arcSegPoints[i] = [
new Point(pt.x + f * sinAngle, pt.y - f * cosAngle),
pt,
new Point(pt.x - f * sinAngle, pt.y + f * cosAngle)
];
angle += deltaTeta;
}
arcSegPoints[0][0] = arcSegPoints[0][1].clone();
arcSegPoints[arcSegPoints.length - 1][2] = arcSegPoints[arcSegPoints.length - 1][1].clone();
mat = /** @type {any} */
new Matrix(0, 0, 0, 0, 0, 0).rotate(xAxisRotation).scale(rx, ry).rotate(-xAxisRotation);
for (i = 0, il = arcSegPoints.length; i < il; i++) {
arcSegPoints[i][0] = arcSegPoints[i][0].transform(mat);
arcSegPoints[i][1] = arcSegPoints[i][1].transform(mat);
arcSegPoints[i][2] = arcSegPoints[i][2].transform(mat);
}
for (i = 1, il = arcSegPoints.length; i < il; i++) {
pt = arcSegPoints[i - 1][2];
x1 = pt.x;
y1 = pt.y;
pt = arcSegPoints[i][0];
x2 = pt.x;
y2 = pt.y;
pt = arcSegPoints[i][1];
x = pt.x;
y = pt.y;
retVal.push(["C", x1, y1, x2, y2, x, y]);
}
return retVal;
}
function handleBlock(startArr, startOffsetM, startOffsetNextM, destArr, destOffsetM, destOffsetNextM) {
var startArrTemp = startArr.slice(startOffsetM, startOffsetNextM || void 0);
var destArrTemp = destArr.slice(destOffsetM, destOffsetNextM || void 0);
var i = 0, posStart = { pos: [0, 0], start: [0, 0] }, posDest = { pos: [0, 0], start: [0, 0] };
while (true) {
startArrTemp[i] = simplify.call(posStart, startArrTemp[i]);
destArrTemp[i] = simplify.call(posDest, destArrTemp[i]);
if (startArrTemp[i][0] != destArrTemp[i][0] || startArrTemp[i][0] == "M" || startArrTemp[i][0] == "A" && (startArrTemp[i][4] != destArrTemp[i][4] || startArrTemp[i][5] != destArrTemp[i][5])) {
Array.prototype.splice.apply(
startArrTemp,
/** @type {[number, number, ...any[]]} */
[i, 1].concat(
/** @type {any} */
toBezier.call(posStart, startArrTemp[i])
)
);
Array.prototype.splice.apply(
destArrTemp,
/** @type {[number, number, ...any[]]} */
[i, 1].concat(
/** @type {any} */
toBezier.call(posDest, destArrTemp[i])
)
);
} else {
startArrTemp[i] = /** @type {any} */
setPosAndReflection.call(
posStart,
startArrTemp[i]
);
destArrTemp[i] = /** @type {any} */
setPosAndReflection.call(
posDest,
destArrTemp[i]
);
}
if (++i == startArrTemp.length && i == destArrTemp.length) break;
if (i == startArrTemp.length) {
startArrTemp.push([
"C",
posStart.pos[0],
posStart.pos[1],
posStart.pos[0],
posStart.pos[1],
posStart.pos[0],
posStart.pos[1]
]);
}
if (i == destArrTemp.length) {
destArrTemp.push([
"C",
posDest.pos[0],
posDest.pos[1],
posDest.pos[0],
posDest.pos[1],
posDest.pos[0],
posDest.pos[1]
]);
}
}
return { start: startArrTemp, dest: destArrTemp };
}
function synchronizePaths(fromD, toD) {
var startArr = parsePath(fromD);
var destArr = parsePath(toD);
var startOffsetM = 0;
var destOffsetM = 0;
var startOffsetNextM = false;
var destOffsetNextM = false;
var result;
while (true) {
if (startOffsetM === false && destOffsetM === false) break;
startOffsetNextM = findNextM(
startArr,
startOffsetM === false ? false : startOffsetM + 1
);
destOffsetNextM = findNextM(
destArr,
destOffsetM === false ? false : destOffsetM + 1
);
if (startOffsetM === false) {
const bbox = pathBbox(
/** @type {any} */
result.start
);
if (bbox.height == 0 || bbox.width == 0) {
startOffsetM = startArr.push(startArr[0]) - 1;
} else {
startOffsetM = startArr.push([
"M",
bbox.x + bbox.width / 2,
bbox.y + bbox.height / 2
]) - 1;
}
}
if (destOffsetM === false) {
const bbox = pathBbox(
/** @type {any} */
result.dest
);
if (bbox.height == 0 || bbox.width == 0) {
destOffsetM = destArr.push(destArr[0]) - 1;
} else {
destOffsetM = destArr.push([
"M",
bbox.x + bbox.width / 2,
bbox.y + bbox.height / 2
]) - 1;
}
}
result = handleBlock(
startArr,
startOffsetM,
startOffsetNextM,
destArr,
destOffsetM,
destOffsetNextM
);
startArr = startArr.slice(0, startOffsetM).concat(
result.start,
startOffsetNextM === false ? [] : startArr.slice(startOffsetNextM)
);
destArr = destArr.slice(0, destOffsetM).concat(
result.dest,
destOffsetNextM === false ? [] : destArr.slice(destOffsetNextM)
);
startOffsetM = startOffsetNextM === false ? false : startOffsetM + result.start.length;
destOffsetM = destOffsetNextM === false ? false : destOffsetM + result.dest.length;
}
return { start: startArr, dest: destArr };
}
function morphPaths(fromD, toD) {
var synced = synchronizePaths(fromD, toD);
var startArr = synced.start;
var destArr = synced.dest;
return function(pos) {
var result = startArr.map(function(from, idx) {
return destArr[idx].map(function(to, toIdx) {
if (toIdx === 0) return to;
return from[toIdx] + (destArr[idx][toIdx] - from[toIdx]) * pos;
});
});
return arrayToPath(result);
};
}
function easeInOut(t) {
return -Math.cos(t * Math.PI) / 2 + 0.5;
}
function parseColor(str) {
if (!str || typeof str !== "string") return null;
if (str[0] === "#") {
let hex = str.slice(1);
if (hex.length === 3)
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
const n = parseInt(hex, 16);
return [n >> 16 & 255, n >> 8 & 255, n & 255, 1];
}
const m = str.match(
/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+))?\s*\)/
);
if (m) return [+m[1], +m[2], +m[3], m[4] !== void 0 ? +m[4] : 1];
return null;
}
function interpolateColor(from, to, pos) {
return `rgba(${Math.round(from[0] + (to[0] - from[0]) * pos)},${Math.round(from[1] + (to[1] - from[1]) * pos)},${Math.round(from[2] + (to[2] - from[2]) * pos)},${from[3] + (to[3] - from[3]) * pos})`;
}
class SVGAnimationRunner {
/**
* @param {any} element
* @param {number} duration
* @param {number} delay
*/
constructor(element, duration, delay) {
this.el = element;
this.duration = duration != null ? duration : 300;
this.delay = delay || 0;
this._attrTarget = null;
this._plotTarget = null;
this._afterCb = null;
this._duringCb = null;
this._next = null;
this._root = null;
this._scheduled = false;
}
/**
* @param {Record<string, any>} to
*/
attr(to) {
this._attrTarget = to;
this._schedule();
return this;
}
/**
* @param {string} d
*/
plot(d) {
this._plotTarget = d;
this._schedule();
return this;
}
/**
* @param {Function} fn
*/
after(fn) {
this._afterCb = fn;
this._schedule();
return this;
}
/**
* @param {Function} fn
*/
during(fn) {
this._duringCb = fn;
this._schedule();
return this;
}
/**
* @param {number} duration
* @param {number} delay
*/
animate(duration, delay) {
const next = new SVGAnimationRunner(this.el, duration, delay);
this._next = next;
next._root = this._root || this;
return next;
}
_schedule() {
const root = this._root || this;
if (!root._scheduled) {
root._scheduled = true;
queueMicrotask(() => root._executeChain());
}
}
_executeChain() {
const chain = [];
let r = this;
while (r) {
chain.push(r);
r = r._next;
}
let cumulativeDelay = 0;
chain.forEach((runner) => {
cumulativeDelay += runner.delay;
runner._execute(cumulativeDelay);
cumulativeDelay += runner.duration;
});
}
/**
* @param {number} startDelay
*/
_execute(startDelay) {
const el = this.el;
const duration = this.duration;
if (duration <= 1) {
const apply = () => {
if (this._attrTarget) el.attr(this._attrTarget);
if (this._plotTarget) el.plot(this._plotTarget);
if (this._afterCb) this._afterCb.call(el);
};
if (startDelay > 0) {
setTimeout(apply, startDelay);
} else {
apply();
}
return;
}
const run = () => {
const fromAttrs = (
/** @type {Record<string, any>} */
{}
);
const fromColors = (
/** @type {Record<string, any>} */
{}
);
const toColors = (
/** @type {Record<string, any>} */
{}
);
if (this._attrTarget) {
for (const key of Object.keys(this._attrTarget)) {
const fromVal = el.attr(key);
fromAttrs[key] = fromVal;
const fc = parseColor(fromVal);
const tc = parseColor(String(this._attrTarget[key]));
if (fc && tc) {
fromColors[key] = fc;
toColors[key] = tc;
}
}
}
let morphFn = null;
if (this._plotTarget) {
const fromPath = el.attr("d") || "";
try {
morphFn = morphPaths(fromPath, this._plotTarget);
} catch (e) {
morphFn = null;
}
}
const start = performance.now();
const tick = (now) => {
const elapsed = now - start;
const rawPos = Math.min(elapsed / duration, 1);
const pos = easeInOut(rawPos);
if (this._attrTarget) {
if (rawPos >= 1) {
el.attr(this._attrTarget);
} else {
const current = (
/** @type {Record<string, any>} */
{}
);
for (const key of Object.keys(this._attrTarget)) {
if (fromColors[key] && toColors[key]) {
current[key] = interpolateColor(
fromColors[key],
toColors[key],
pos
);
} else {
const from = parseFloat(fromAttrs[key]);
const to = parseFloat(this._attrTarget[key]);
if (!isNaN(from) && !isNaN(to)) {
current[key] = from + (to - from) * pos;
}
}
}
el.attr(current);
}
}
if (morphFn && rawPos < 1) {
el.attr(
"d",
/** @type {any} */
morphFn(pos)
);
}
if (this._duringCb) this._duringCb(pos);
if (rawPos < 1) {
BrowserAPIs.requestAnimationFrame(tick);
} else {
if (this._plotTarget) {
el.attr("d", this._plotTarget);
}
if (this._afterCb) this._afterCb.call(el);
}
};
BrowserAPIs.requestAnimationFrame(tick);
};
if (startDelay > 0) {
setTimeout(run, startDelay);
} else {
run();
}
}
}
function installAnimationMethods(ElementClass) {
ElementClass.prototype.animate = function(duration, delay) {
return new SVGAnimationRunner(this, duration, delay);
};
}
function installDraggable(ElementClass) {
ElementClass.prototype.draggable = function(opts) {
if (opts === false) {
if (this._dragCleanup) {
this._dragCleanup();
this._dragCleanup = null;
}
return this;
}
const el = this;
const constraints = opts || {};
const onPointerDown = (e) => {
if (e.button && e.button !== 0) return;
e.stopPropagation();
const isTouch = e.type === "touchstart";
const ev = isTouch ? e.touches[0] : e;
const svgEl = el.node;
const startAttrX = parseFloat(svgEl.getAttribute("x")) || 0;
const startAttrY = parseFloat(svgEl.getAttribute("y")) || 0;
const startClientX = ev.clientX;
const startClientY = ev.clientY;
const svgRoot = svgEl.ownerSVGElement;
let ctm = null;
if (svgRoot) {
ctm = svgRoot.getScreenCTM();
}
const onMove = (me) => {
const mev = me.type === "touchmove" ? me.touches[0] : me;
let dx = mev.clientX - startClientX;
let dy = mev.clientY - startClientY;
if (ctm) {
dx = dx / ctm.a;
dy = dy / ctm.d;
}
let newX = startAttrX + dx;
let newY = startAttrY + dy;
const w = parseFloat(svgEl.getAttribute("width")) || 0;
const h = parseFloat(svgEl.getAttribute("height")) || 0;
if (constraints.minX !== void 0 && newX < constraints.minX)
newX = constraints.minX;
if (constraints.minY !== void 0 && newY < constraints.minY)
newY = constraints.minY;
if (constraints.maxX !== void 0 && newX + w > constraints.maxX)
newX = constraints.maxX - w;
if (constraints.maxY !== void 0 && newY + h > constraints.maxY)
newY = constraints.maxY - h;
const box = {
x: newX,
y: newY,
w,
h,
x2: newX + w,
y2: newY + h
};
const event = new CustomEvent("dragmove", {
detail: {
handler: {
/**
* @param {number} x
* @param {number} y
*/
move: function(x, y) {
svgEl.setAttribute("x", x);
svgEl.setAttribute("y", y);
}
},
box
}
});
svgEl.dispatchEvent(event);
};
const onUp = () => {
if (Environment.isBrowser()) {
document.removeEventListener("mousemove", onMove);
document.removeEventListener("touchmove", onMove);
document.removeEventListener("mouseup", onUp);
document.removeEventListener("touchend", onUp);
}
};
if (Environment.isBrowser()) {
document.addEventListener("mousemove", onMove);
document.addEventListener("touchmove", onMove);
document.addEventListener("mouseup", onUp);
document.addEventListener("touchend", onUp);
}
};
el.node.addEventListener("mousedown", onPointerDown);
el.node.addEventListener("touchstart", onPointerDown);
el._dragCleanup = () => {
el.node.removeEventListener("mousedown", onPointerDown);
el.node.removeEventListener("touchstart", onPointerDown);
};
return el;
};
}
function installSelectable(ElementClass) {
ElementClass.prototype.select = function(opts) {
if (opts === false) {
if (this._selectCleanup) {
this._selectCleanup();
this._selectCleanup = null;
}
return this;
}
const el = this;
const { createHandle, updateHandle } = opts;
const handleGroup = document.createElementNS(SVGNS, "g");
handleGroup.setAttribute("class", "svg_select_points");
const parent = el.node.parentNode;
if (parent) {
parent.appendChild(handleGroup);
}
const handles = {};
const handleNames = ["t", "b", "l", "r", "lt", "rt", "lb", "rb"];
handleNames.forEach((name2, index) => {
const subGroup = new SVGContainer(document.createElementNS(SVGNS, "g"));
handleGroup.appendChild(subGroup.node);
const handle = createHandle(subGroup, [0, 0], index, [], name2);
handles[name2] = { group: subGroup, handle };
});
const updatePositions = () => {
const x = parseFloat(el.attr("x")) || 0;
const y = parseFloat(el.attr("y")) || 0;
const w = parseFloat(el.attr("width")) || 0;
const h = parseFloat(el.attr("height")) || 0;
const elTransform = el.node.getAttribute("transform");
if (elTransform) {
handleGroup.setAttribute("transform", elTransform);
} else {
handleGroup.removeAttribute("transform");
}
const positions = {
t: [x + w / 2, y],
b: [x + w / 2, y + h],
l: [x, y + h / 2],
r: [x + w, y + h / 2],
lt: [x, y],
rt: [x + w, y],
lb: [x, y + h],
rb: [x + w, y + h]
};
handleNames.forEach((name2) => {
if (handles[name2] && positions[name2]) {
updateHandle(handles[name2].group, positions[name2]);
}
});
};
updatePositions();
el._selectHandles = handleGroup;
el._selectHandlesMap = handles;
el._updateSelectPositions = updatePositions;
el._selectCleanup = () => {
if (handleGroup.parentNode) {
handleGroup.parentNode.removeChild(handleGroup);
}
el._selectHandles = null;
el._selectHandlesMap = null;
el._updateSelectPositions = null;
};
return el;
};
ElementClass.prototype.resize = function(enable) {
if (enable === false) {
if (this._resizeCleanup) {
this._resizeCleanup();
this._resizeCleanup = null;
}
return this;
}
const el = this;
const handles = el._selectHandlesMap;
if (!handles) return el;
const cleanupFns = [];
const makeHandleDraggable = (name2) => {
const handleInfo = handles[name2];
if (!handleInfo || !handleInfo.group || !handleInfo.group.node) return;
const handleNode = handleInfo.group.node;
const onPointerDown = (e) => {
if (e.button && e.button !== 0) return;
e.stopPropagation();
const isTouch = e.type === "touchstart";
const ev = isTouch ? e.touches[0] : e;
const startClientX = ev.clientX;
const svgRoot = el.node.ownerSVGElement;
let ctm = null;
if (svgRoot) {
ctm = svgRoot.getScreenCTM();
}
const startX = parseFloat(el.attr("x")) || 0;
const startW = parseFloat(el.attr("width")) || 0;
const onMove = (me) => {
const mev = me.type === "touchmove" ? me.touches[0] : me;
let dx = mev.clientX - startClientX;
if (ctm) dx = dx / /** @type {any} */
ctm.a;
let newX = startX;
let newW = startW;
if (name2 === "l") {
newX = startX + dx;
newW = startW - dx;
} else if (name2 === "r") {
newW = startW + dx;
}
if (newW < 0) {
newW = 0;
}
el.attr({ x: newX, width: newW });
if (el._updateSelectPositions) {
el._updateSelectPositions();
}
const event = new CustomEvent("resize", {
detail: { el }
});
el.node.dispatchEvent(event);
};
const onUp = () => {
if (Environment.isBrowser()) {
document.removeEventListener("mousemove", onMove);
document.removeEventListener("touchmove", onMove);
document.removeEventListener("mouseup", onUp);
document.removeEventListener("touchend", onUp);
}
const event = new CustomEvent("resize", {
detail: { el }
});
el.node.dispatchEvent(event);
};
if (Environment.isBrowser()) {
document.addEventListener("mousemove", onMove);
document.addEventListener("touchmove", onMove);
document.addEventListener("mouseup", onUp);
document.addEventListener("touchend", onUp);
}
};
handleNode.addEventListener("mousedown", onPointerDown);
handleNode.addEventListener("touchstart", onPointerDown);
cleanupFns.push(() => {
handleNode.removeEventListener("mousedown", onPointerDown);
handleNode.removeEventListener("touchstart", onPointerDown);
});
};
makeHandleDraggable("l");
makeHandleDraggable("r");
el._resizeCleanup = () => {
cleanupFns.forEach((fn) => fn());
};
return el;
};
}
installFilterMethods(SVGElement$1);
installAnimationMethods(SVGElement$1);
installDraggable(SVGElement$1);
installSelectable(SVGElement$1);
function SVG() {
const svgEl = BrowserAPIs.createElementNS(SVGNS, "svg");
const svg = new SVGContainer(svgEl);
svg.attr({ xmlns: SVGNS });
return svg;
}
SVG.xlink = "http://www.w3.org/1999/xlink";
if (Environment.isBrowser() && typeof window.SVG === "undefined") {
window.SVG = SVG;
}
if (Environment.isBrowser()) {
if (typeof window.SVG === "undefined") {
window.SVG = SVG;
}
if (typeof window.Apex === "undefined") {
window.Apex = {};
}
} else {
if (typeof global !== "undefined") {
if (typeof /** @type {any} */
global.Apex === "undefined") {
global.Apex = {};
}
if (typeof /** @type {any} */
global.SVG === "undefined") {
global.SVG = SVG;
}
}
}
const _InitCtxVariables = class _InitCtxVariables {
/**
* Register one or more optional feature modules.
*
* @param {Record<string, new (w: object, ctx: object) => unknown>} featureMap
* Plain object mapping ctx property name → constructor.
*
* Example (called from src/features/legend.js):
* InitCtxVariables.registerFeatures({ legend: Legend })
*/
static registerFeatures(featureMap) {
for (const [key, Ctor] of Object.entries(featureMap)) {
_InitCtxVariables._featureRegistry.set(key, Ctor);
}
}
/**
* @param {import('../../types/internal').ChartContext} ctx
*/
constructor(ctx) {
this.ctx = ctx;
this.w = ctx.w;
}
initModules() {
this.ctx.publicMethods = [
"updateOptions",
"updateSeries",
"appendData",
"appendSeries",
"isSeriesHidden",
"highlightSeries",
"toggleSeries",
"showSeries",
"hideSeries",
"setLocale",
"resetSeries",
"zoomX",
"toggleDataPointSelection",
"dataURI",
"exportToCSV",
"addXaxisAnnotation",
"addYaxisAnnotation",
"addPointAnnotation",
"clearAnnotations",
"removeAnnotation",
"paper",
"destroy"
];
this.ctx.eventList = [
"click",
"mousedown",
"mousemove",
"mouseleave",
"touchstart",
"touchmove",
"touchleave",
"mouseup",
"touchend",
"keydown",
"keyup"
];
this.ctx.animations = new Animations(this.w, this.ctx);
this.ctx.axes = new Axes(this.w, this.ctx);
this.ctx.core = new Core(this.ctx.el, this.w, this.ctx);
this.ctx.config = new Config({});
this.ctx.data = new Data(this.w, {
resetGlobals: () => this.ctx.core.resetGlobals(),
isMultipleY: () => this.ctx.core.isMultipleY()
});
this.ctx.grid = new Grid(this.w, this.ctx);
this.ctx.graphics = new Graphics(this.w, this.ctx);
this.ctx.coreUtils = new CoreUtils(this.w);
this.ctx.crosshairs = new Crosshairs(this.w);
this.ctx.events = new Events(this.w, this.ctx);
this.ctx.fill = new Fill(this.w);
this.ctx.localization = new Localization(this.w);
this.ctx.options = new Options();
this.ctx.responsive = new Responsive(this.w);
this.ctx.series = new Series(this.w, {
// legend may not be registered — guard with ?.
toggleDataSeries: (...a) => {
var _a;
return (_a = this.ctx.legend) == null ? void 0 : _a.legendHelpers.toggleDataSeries(...a);
},
revertDefaultAxisMinMax: () => this.ctx.updateHelpers.revertDefaultAxisMinMax(),
updateSeries: (...a) => this.ctx.updateHelpers._updateSeries(...a)
});
this.ctx.theme = new Theme(this.w);
this.ctx.formatters = new Formatters(this.w);
this.ctx.titleSubtitle = new TitleSubtitle(this.w);
this.ctx.dimensions = new Dimensions(this.w, this.ctx);
this.ctx.updateHelpers = new UpdateHelpers(this.w, this.ctx);
const tooltipInstance = new Tooltip(this.w, this.ctx);
this.w.globals.tooltip = tooltipInstance;
Object.defineProperty(this.ctx, "tooltip", {
get() {
return this.w.globals.tooltip;
},
configurable: true
});
this._initOptionalModules();
}
/**
* Instantiate optional feature modules from the registry.
*
* Modules that are not registered are set to null on ctx so that call sites
* can safely use optional-chaining (ctx.tooltip?.drawTooltip(...)).
*
* Lazy-getter features (toolbar, zoomPanSelection, keyboardNavigation) are
* installed as on-demand getters so they are only constructed if accessed,
* and only if their constructor was registered.
*/
_initOptionalModules() {
const reg = _InitCtxVariables._featureRegistry;
const w = this.w;
const ctx = this.ctx;
const ExportsCtor = reg.get("exports");
ctx.exports = ExportsCtor ? new ExportsCtor(w, ctx) : null;
const LegendCtor = reg.get("legend");
ctx.legend = LegendCtor ? new LegendCtor(w, ctx) : null;
const ToolbarCtor = reg.get("toolbar");
Object.defineProperty(ctx, "toolbar", {
get() {
var _a;
if (!this._toolbar && ToolbarCtor)
this._toolbar = new ToolbarCtor(w, this);
return (_a = this._toolbar) != null ? _a : null;
},
configurable: true
});
const ZoomPanCtor = reg.get("zoomPanSelection");
Object.defineProperty(ctx, "zoomPanSelection", {
get() {
var _a;
if (!this._zoomPanSelection && ZoomPanCtor)
this._zoomPanSelection = new ZoomPanCtor(w, this);
return (_a = this._zoomPanSelection) != null ? _a : null;
},
configurable: true
});
const KeyboardCtor = reg.get("keyboardNavigation");
Object.defineProperty(ctx, "keyboardNavigation", {
get() {
var _a;
if (!this._keyboardNavigation && KeyboardCtor)
this._keyboardNavigation = new KeyboardCtor(w, this);
return (_a = this._keyboardNavigation) != null ? _a : null;
},
configurable: true
});
}
};
/**
* Registry of optional feature modules.
*
* Populated by ApexCharts.registerFeatures() (called from feature entry
* files such as src/features/legend.js). Keys match the ctx property name
* the module is stored under (e.g. 'legend', 'exports').
*
* Core modules that every chart needs are NOT in this registry — they are
* always instantiated unconditionally in initModules().
*/
__publicField(_InitCtxVariables, "_featureRegistry", /* @__PURE__ */ new Map());
let InitCtxVariables = _InitCtxVariables;
class Destroy {
/**
* @param {import('../../types/internal').ChartContext} ctx
*/
constructor(ctx) {
this.ctx = ctx;
this.w = ctx.w;
}
/**
* @param {{ isUpdating: boolean }} opts
*/
clear({ isUpdating }) {
if (this.ctx._zoomPanSelection) {
this.ctx._zoomPanSelection.destroy();
}
if (this.ctx._toolbar) {
this.ctx._toolbar.destroy();
}
if (this.w.globals.resizeObserver && typeof this.w.globals.resizeObserver.disconnect === "function") {
this.w.globals.resizeObserver.disconnect();
this.w.globals.resizeObserver = null;
}
PerformanceCache.invalidateAll(this.w);
if (isUpdating) {
this.ctx._zoomPanSelection = null;
this.ctx._toolbar = null;
this.ctx._keyboardNavigation = null;
} else {
this.ctx.animations = null;
this.ctx.axes = null;
this.ctx.annotations = null;
this.ctx.core = null;
this.ctx.data = null;
this.ctx.grid = null;
this.ctx.series = null;
this.ctx.responsive = null;
this.ctx.theme = null;
this.ctx.formatters = null;
this.ctx.titleSubtitle = null;
this.ctx.legend = null;
this.ctx.dimensions = null;
this.ctx.options = null;
this.ctx.crosshairs = null;
this.ctx._zoomPanSelection = null;
this.ctx.updateHelpers = null;
this.ctx._toolbar = null;
this.ctx.localization = null;
this.ctx._keyboardNavigation = null;
this.ctx.w.globals.tooltip = null;
}
this.clearDomElements({ isUpdating });
}
/**
* @param {any} draw
*/
killSVG(draw) {
draw.each(
/** @this {any} */
function() {
this.removeClass("*");
this.off();
},
true
);
draw.clear();
}
/**
* @param {{ isUpdating: boolean }} opts
*/
clearDomElements({ isUpdating }) {
const domEls = (
/** @type {any} */
this.w.dom
);
if (Environment.isBrowser()) {
const elSVG = domEls.Paper.node;
if (elSVG.parentNode && elSVG.parentNode.parentNode && !isUpdating) {
elSVG.parentNode.parentNode.style.minHeight = "unset";
}
const baseEl = domEls.baseEl;
if (baseEl) {
this.ctx.eventList.forEach((event) => {
baseEl.removeEventListener(event, this.ctx.events.documentEvent);
});
}
if (this.ctx.el !== null) {
while (this.ctx.el.firstChild) {
this.ctx.el.removeChild(this.ctx.el.firstChild);
}
}
this.killSVG(domEls.Paper);
domEls.Paper.remove();
}
domEls.elWrap = null;
domEls.elGraphical = null;
domEls.elLegendWrap = null;
domEls.elLegendForeign = null;
domEls.baseEl = null;
domEls.elGridRect = null;
domEls.elGridRectMask = null;
domEls.elGridRectBarMask = null;
domEls.elGridRectMarkerMask = null;
domEls.elForecastMask = null;
domEls.elNonForecastMask = null;
domEls.elDefs = null;
}
}
const ros = /* @__PURE__ */ new WeakMap();
function addResizeListener(el, fn) {
if (Environment.isSSR()) return;
let called = false;
if (el.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) {
const elRect = el.getBoundingClientRect();
if (el.style.display === "none" || elRect.width === 0) {
called = true;
}
}
const ro = new ResizeObserver((r) => {
if (called) {
fn.call(el, r);
}
called = true;
});
if (el.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
Array.from(el.children).forEach((c) => ro.observe(c));
} else {
ro.observe(el);
}
ros.set(fn, ro);
}
function removeResizeListener(el, fn) {
if (Environment.isSSR()) return;
const ro = ros.get(fn);
if (ro) {
ro.disconnect();
ros.delete(fn);
}
}
const apexCSS = `@keyframes opaque {
0% {
opacity: 0
}
to {
opacity: 1
}
}
@keyframes resizeanim {
0%,
to {
opacity: 0
}
}
.apexcharts-canvas {
position: relative;
direction: ltr !important;
user-select: none;
/* Focus indicator colour. Themes override below. */
--apexcharts-focus-color: #008FFB;
}
/* Dark theme & high-contrast: brighter focus colour for sufficient contrast. */
.apexcharts-canvas .apexcharts-theme-dark,
.apexcharts-theme-dark.apexcharts-canvas {
--apexcharts-focus-color: #FFD500;
}
.apexcharts-canvas.apexcharts-high-contrast,
.apexcharts-high-contrast.apexcharts-canvas {
--apexcharts-focus-color: #FFFF00;
}
/* Visually-hidden aria-live status region (WCAG 4.1.3 Status Messages). */
.apexcharts-sr-status {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
/* Respect OS-level reduced-motion preference (WCAG 2.3.3). */
@media (prefers-reduced-motion: reduce) {
.apexcharts-canvas *,
.apexcharts-canvas *::before,
.apexcharts-canvas *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
.apexcharts-canvas ::-webkit-scrollbar {
-webkit-appearance: none;
width: 6px
}
.apexcharts-canvas ::-webkit-scrollbar-thumb {
border-radius: 4px;
background-color: rgba(0, 0, 0, .5);
box-shadow: 0 0 1px rgba(255, 255, 255, .5);
-webkit-box-shadow: 0 0 1px rgba(255, 255, 255, .5)
}
.apexcharts-inner {
position: relative
}
.apexcharts-text tspan {
font-family: inherit
}
rect.legend-mouseover-inactive,
.legend-mouseover-inactive rect,
.legend-mouseover-inactive path,
.legend-mouseover-inactive circle,
.legend-mouseover-inactive line,
.legend-mouseover-inactive text.apexcharts-yaxis-title-text,
.legend-mouseover-inactive text.apexcharts-yaxis-label {
transition: .15s ease all;
opacity: .2
}
.apexcharts-legend-text {
padding-left: 15px;
margin-left: -15px;
}
.apexcharts-legend-series[role="button"]:focus {
outline: 2px solid var(--apexcharts-focus-color, #008FFB);
outline-offset: 2px;
}
.apexcharts-legend-series[role="button"]:focus:not(:focus-visible) {
outline: none;
}
.apexcharts-legend-series[role="button"]:focus-visible {
outline: 2px solid var(--apexcharts-focus-color, #008FFB);
outline-offset: 2px;
}
.apexcharts-series-collapsed {
opacity: 0
}
.apexcharts-canvas svg:focus:not(:focus-visible) {
outline: none;
}
/* Keyboard navigation focus indicator on SVG data elements.
SVG elements don't support CSS outline, so we use stroke. */
.apexcharts-bar-area.apexcharts-keyboard-focused,
.apexcharts-candlestick-area.apexcharts-keyboard-focused,
.apexcharts-boxPlot-area.apexcharts-keyboard-focused,
.apexcharts-rangebar-area.apexcharts-keyboard-focused,
.apexcharts-pie-area.apexcharts-keyboard-focused,
.apexcharts-heatmap-rect.apexcharts-keyboard-focused,
.apexcharts-treemap-rect.apexcharts-keyboard-focused {
stroke: var(--apexcharts-focus-color, #008FFB);
stroke-width: 2;
stroke-opacity: 1;
}
.apexcharts-tooltip {
border-radius: 5px;
box-shadow: 2px 2px 6px -4px #999;
cursor: default;
font-size: 14px;
left: 62px;
opacity: 0;
pointer-events: none;
position: absolute;
top: 20px;
display: flex;
flex-direction: column;
overflow: hidden;
white-space: nowrap;
z-index: 12;
transition: .15s ease all
}
.apexcharts-tooltip.apexcharts-active {
opacity: 1;
transition: .15s ease all
}
.apexcharts-tooltip.apexcharts-theme-light {
border: 1px solid #e3e3e3;
background: rgba(255, 255, 255, .96)
}
.apexcharts-tooltip.apexcharts-theme-dark {
color: #fff;
background: rgba(30, 30, 30, .8)
}
.apexcharts-tooltip * {
font-family: inherit
}
.apexcharts-tooltip-title {
padding: 6px;
font-size: 15px;
margin-bottom: 4px
}
.apexcharts-tooltip.apexcharts-theme-light .apexcharts-tooltip-title {
background: #eceff1;
border-bottom: 1px solid #ddd
}
.apexcharts-tooltip.apexcharts-theme-dark .apexcharts-tooltip-title {
background: rgba(0, 0, 0, .7);
border-bottom: 1px solid #333
}
.apexcharts-tooltip-text-goals-value,
.apexcharts-tooltip-text-y-value,
.apexcharts-tooltip-text-z-value {
display: inline-block;
margin-left: 5px;
font-weight: 600
}
.apexcharts-tooltip-text-goals-label:empty,
.apexcharts-tooltip-text-goals-value:empty,
.apexcharts-tooltip-text-y-label:empty,
.apexcharts-tooltip-text-y-value:empty,
.apexcharts-tooltip-text-z-value:empty,
.apexcharts-tooltip-title:empty {
display: none
}
.apexcharts-tooltip-text-goals-label,
.apexcharts-tooltip-text-goals-value {
padding: 6px 0 5px
}
.apexcharts-tooltip-goals-group,
.apexcharts-tooltip-text-goals-label,
.apexcharts-tooltip-text-goals-value {
display: flex
}
.apexcharts-tooltip-text-goals-label:not(:empty),
.apexcharts-tooltip-text-goals-value:not(:empty) {
margin-top: -6px
}
.apexcharts-tooltip-marker {
display: inline-block;
position: relative;
width: 16px;
height: 16px;
font-size: 16px;
line-height: 16px;
margin-right: 4px;
text-align: center;
vertical-align: middle;
color: inherit;
}
.apexcharts-tooltip-marker::before {
content: "";
display: inline-block;
width: 100%;
text-align: center;
color: currentcolor;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
font-size: 26px;
font-family: Arial, Helvetica, sans-serif;
line-height: 14px;
font-weight: 900;
}
.apexcharts-tooltip-marker[shape="circle"]::before {
content: "\\25CF";
}
.apexcharts-tooltip-marker[shape="square"]::before,
.apexcharts-tooltip-marker[shape="rect"]::before {
content: "\\25A0";
transform: translate(-1px, -2px);
}
.apexcharts-tooltip-marker[shape="line"]::before {
content: "\\2500";
}
.apexcharts-tooltip-marker[shape="diamond"]::before {
content: "\\25C6";
font-size: 28px;
}
.apexcharts-tooltip-marker[shape="triangle"]::before {
content: "\\25B2";
font-size: 22px;
}
.apexcharts-tooltip-marker[shape="cross"]::before {
content: "\\2715";
font-size: 18px;
}
.apexcharts-tooltip-marker[shape="plus"]::before {
content: "\\2715";
transform: rotate(45deg) translate(-1px, -1px);
font-size: 18px;
}
.apexcharts-tooltip-marker[shape="star"]::before {
content: "\\2605";
font-size: 18px;
}
.apexcharts-tooltip-marker[shape="sparkle"]::before {
content: "\\2726";
font-size: 20px;
}
.apexcharts-tooltip-series-group {
padding: 0 10px;
display: none;
text-align: left;
justify-content: left;
align-items: center
}
.apexcharts-tooltip-series-group.apexcharts-active .apexcharts-tooltip-marker {
opacity: 1
}
.apexcharts-tooltip-series-group.apexcharts-active,
.apexcharts-tooltip-series-group:last-child {
padding-bottom: 4px
}
.apexcharts-tooltip-y-group {
padding: 6px 0 5px
}
.apexcharts-custom-tooltip,
.apexcharts-tooltip-box {
padding: 4px 8px
}
.apexcharts-tooltip-boxPlot {
display: flex;
flex-direction: column-reverse
}
.apexcharts-tooltip-box>div {
margin: 4px 0
}
.apexcharts-tooltip-box span.value {
font-weight: 700
}
.apexcharts-tooltip-rangebar {
padding: 5px 8px
}
.apexcharts-tooltip-rangebar .category {
font-weight: 600;
color: #777
}
.apexcharts-tooltip-rangebar .series-name {
font-weight: 700;
display: block;
margin-bottom: 5px
}
.apexcharts-xaxistooltip,
.apexcharts-yaxistooltip {
opacity: 0;
pointer-events: none;
color: #373d3f;
font-size: 13px;
text-align: center;
border-radius: 2px;
position: absolute;
z-index: 10;
background: #eceff1;
border: 1px solid #90a4ae
}
.apexcharts-xaxistooltip {
padding: 9px 10px;
transition: .15s ease all
}
.apexcharts-xaxistooltip.apexcharts-theme-dark {
background: rgba(0, 0, 0, .7);
border: 1px solid rgba(0, 0, 0, .5);
color: #fff
}
.apexcharts-xaxistooltip:after,
.apexcharts-xaxistooltip:before {
left: 50%;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none
}
.apexcharts-xaxistooltip:after {
border-color: transparent;
border-width: 6px;
margin-left: -6px
}
.apexcharts-xaxistooltip:before {
border-color: transparent;
border-width: 7px;
margin-left: -7px
}
.apexcharts-xaxistooltip-bottom:after,
.apexcharts-xaxistooltip-bottom:before {
bottom: 100%
}
.apexcharts-xaxistooltip-top:after,
.apexcharts-xaxistooltip-top:before {
top: 100%
}
.apexcharts-xaxistooltip-bottom:after {
border-bottom-color: #eceff1
}
.apexcharts-xaxistooltip-bottom:before {
border-bottom-color: #90a4ae
}
.apexcharts-xaxistooltip-bottom.apexcharts-theme-dark:after,
.apexcharts-xaxistooltip-bottom.apexcharts-theme-dark:before {
border-bottom-color: rgba(0, 0, 0, .5)
}
.apexcharts-xaxistooltip-top:after {
border-top-color: #eceff1
}
.apexcharts-xaxistooltip-top:before {
border-top-color: #90a4ae
}
.apexcharts-xaxistooltip-top.apexcharts-theme-dark:after,
.apexcharts-xaxistooltip-top.apexcharts-theme-dark:before {
border-top-color: rgba(0, 0, 0, .5)
}
.apexcharts-xaxistooltip.apexcharts-active {
opacity: 1;
transition: .15s ease all
}
.apexcharts-yaxistooltip {
padding: 4px 10px
}
.apexcharts-yaxistooltip.apexcharts-theme-dark {
background: rgba(0, 0, 0, .7);
border: 1px solid rgba(0, 0, 0, .5);
color: #fff
}
.apexcharts-yaxistooltip:after,
.apexcharts-yaxistooltip:before {
top: 50%;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none
}
.apexcharts-yaxistooltip:after {
border-color: transparent;
border-width: 6px;
margin-top: -6px
}
.apexcharts-yaxistooltip:before {
border-color: transparent;
border-width: 7px;
margin-top: -7px
}
.apexcharts-yaxistooltip-left:after,
.apexcharts-yaxistooltip-left:before {
left: 100%
}
.apexcharts-yaxistooltip-right:after,
.apexcharts-yaxistooltip-right:before {
right: 100%
}
.apexcharts-yaxistooltip-left:after {
border-left-color: #eceff1
}
.apexcharts-yaxistooltip-left:before {
border-left-color: #90a4ae
}
.apexcharts-yaxistooltip-left.apexcharts-theme-dark:after,
.apexcharts-yaxistooltip-left.apexcharts-theme-dark:before {
border-left-color: rgba(0, 0, 0, .5)
}
.apexcharts-yaxistooltip-right:after {
border-right-color: #eceff1
}
.apexcharts-yaxistooltip-right:before {
border-right-color: #90a4ae
}
.apexcharts-yaxistooltip-right.apexcharts-theme-dark:after,
.apexcharts-yaxistooltip-right.apexcharts-theme-dark:before {
border-right-color: rgba(0, 0, 0, .5)
}
.apexcharts-yaxistooltip.apexcharts-active {
opacity: 1
}
.apexcharts-yaxistooltip-hidden {
display: none
}
.apexcharts-xcrosshairs,
.apexcharts-ycrosshairs {
pointer-events: none;
opacity: 0;
transition: .15s ease all
}
.apexcharts-xcrosshairs.apexcharts-active,
.apexcharts-ycrosshairs.apexcharts-active {
opacity: 1;
transition: .15s ease all
}
.apexcharts-ycrosshairs-hidden {
opacity: 0
}
.apexcharts-selection-rect {
cursor: move
}
.svg_select_shape {
stroke-width: 1;
stroke-dasharray: 10 10;
stroke: black;
stroke-opacity: 0.1;
pointer-events: none;
fill: none;
}
.svg_select_handle {
stroke-width: 3;
stroke: black;
fill: none;
}
.svg_select_handle_r {
cursor: e-resize;
}
.svg_select_handle_l {
cursor: w-resize;
}
.apexcharts-svg.apexcharts-zoomable.hovering-zoom {
cursor: crosshair
}
.apexcharts-svg.apexcharts-zoomable.hovering-pan {
cursor: move
}
.apexcharts-menu-icon,
.apexcharts-pan-icon,
.apexcharts-reset-icon,
.apexcharts-selection-icon,
.apexcharts-toolbar-custom-icon,
.apexcharts-zoom-icon,
.apexcharts-zoomin-icon,
.apexcharts-zoomout-icon {
cursor: pointer;
/* WCAG 2.5.8 Target Size (Minimum): 24×24 CSS px hit target. */
width: 24px;
height: 24px;
line-height: 24px;
color: #6e8192;
text-align: center;
/* Reset native <button> chrome — these are styled via SVG icons. */
padding: 0;
margin: 0;
background: transparent;
border: 0;
display: inline-flex;
align-items: center;
justify-content: center;
}
.apexcharts-menu-icon svg,
.apexcharts-reset-icon svg,
.apexcharts-zoom-icon svg,
.apexcharts-zoomin-icon svg,
.apexcharts-zoomout-icon svg {
fill: #6e8192
}
.apexcharts-selection-icon svg {
fill: #444;
transform: scale(.76)
}
.apexcharts-theme-dark .apexcharts-menu-icon svg,
.apexcharts-theme-dark .apexcharts-pan-icon svg,
.apexcharts-theme-dark .apexcharts-reset-icon svg,
.apexcharts-theme-dark .apexcharts-selection-icon svg,
.apexcharts-theme-dark .apexcharts-toolbar-custom-icon svg,
.apexcharts-theme-dark .apexcharts-zoom-icon svg,
.apexcharts-theme-dark .apexcharts-zoomin-icon svg,
.apexcharts-theme-dark .apexcharts-zoomout-icon svg {
fill: #f3f4f5
}
.apexcharts-canvas .apexcharts-reset-zoom-icon.apexcharts-selected svg,
.apexcharts-canvas .apexcharts-selection-icon.apexcharts-selected svg,
.apexcharts-canvas .apexcharts-zoom-icon.apexcharts-selected svg {
fill: #008ffb
}
.apexcharts-theme-light .apexcharts-menu-icon:hover svg,
.apexcharts-theme-light .apexcharts-reset-icon:hover svg,
.apexcharts-theme-light .apexcharts-selection-icon:not(.apexcharts-selected):hover svg,
.apexcharts-theme-light .apexcharts-zoom-icon:not(.apexcharts-selected):hover svg,
.apexcharts-theme-light .apexcharts-zoomin-icon:hover svg,
.apexcharts-theme-light .apexcharts-zoomout-icon:hover svg {
fill: #333
}
.apexcharts-menu-icon,
.apexcharts-selection-icon {
position: relative
}
.apexcharts-reset-icon {
margin-left: 5px
}
.apexcharts-menu-icon,
.apexcharts-reset-icon,
.apexcharts-zoom-icon {
transform: scale(.85)
}
.apexcharts-zoomin-icon,
.apexcharts-zoomout-icon {
transform: scale(.7)
}
.apexcharts-zoomout-icon {
margin-right: 3px
}
.apexcharts-pan-icon {
transform: scale(.62);
position: relative;
left: 1px;
top: 0
}
.apexcharts-pan-icon svg {
fill: #fff;
stroke: #6e8192;
stroke-width: 2
}
.apexcharts-pan-icon.apexcharts-selected svg {
stroke: #008ffb
}
.apexcharts-pan-icon:not(.apexcharts-selected):hover svg {
stroke: #333
}
.apexcharts-toolbar {
position: absolute;
z-index: 11;
max-width: 176px;
text-align: right;
border-radius: 3px;
padding: 0 6px 2px;
display: flex;
justify-content: space-between;
align-items: center
}
.apexcharts-menu {
background: #fff;
position: absolute;
top: 100%;
border: 1px solid #ddd;
border-radius: 3px;
padding: 3px;
right: 10px;
opacity: 0;
min-width: 110px;
transition: .15s ease all;
pointer-events: none
}
.apexcharts-menu.apexcharts-menu-open {
opacity: 1;
pointer-events: all;
transition: .15s ease all
}
.apexcharts-menu-item {
padding: 6px 7px;
font-size: 12px;
cursor: pointer
}
.apexcharts-theme-light .apexcharts-menu-item:hover {
background: #eee
}
.apexcharts-theme-dark .apexcharts-menu {
background: rgba(0, 0, 0, .7);
color: #fff
}
@media screen and (min-width:768px) {
.apexcharts-canvas:hover .apexcharts-toolbar {
opacity: 1
}
}
/* Toolbar keyboard accessibility: show toolbar when any button inside it is focused */
.apexcharts-toolbar:focus-within {
opacity: 1
}
/* Focus indicator for toolbar icon buttons */
.apexcharts-menu-icon:focus-visible,
.apexcharts-pan-icon:focus-visible,
.apexcharts-reset-icon:focus-visible,
.apexcharts-selection-icon:focus-visible,
.apexcharts-toolbar-custom-icon:focus-visible,
.apexcharts-zoom-icon:focus-visible,
.apexcharts-zoomin-icon:focus-visible,
.apexcharts-zoomout-icon:focus-visible {
outline: 2px solid var(--apexcharts-focus-color, #008FFB);
outline-offset: 2px;
border-radius: 2px
}
/* Focus indicator for hamburger menu items */
.apexcharts-menu-item:focus-visible {
outline: 2px solid var(--apexcharts-focus-color, #008FFB);
outline-offset: -2px;
background: #eee
}
.apexcharts-canvas .apexcharts-element-hidden,
.apexcharts-datalabel.apexcharts-element-hidden,
.apexcharts-hide .apexcharts-series-points {
opacity: 0;
}
.apexcharts-hidden-element-shown {
opacity: 1;
transition: 0.25s ease all;
}
.apexcharts-datalabel,
.apexcharts-datalabel-label,
.apexcharts-datalabel-value,
.apexcharts-datalabels,
.apexcharts-pie-label {
cursor: default;
pointer-events: none
}
.apexcharts-pie-label-delay {
opacity: 0;
animation-name: opaque;
animation-duration: .3s;
animation-fill-mode: forwards;
animation-timing-function: ease
}
.apexcharts-radialbar-label {
cursor: pointer;
}
.apexcharts-annotation-rect,
.apexcharts-area-series .apexcharts-area,
.apexcharts-gridline,
.apexcharts-line,
.apexcharts-point-annotation-label,
.apexcharts-radar-series path:not(.apexcharts-marker),
.apexcharts-radar-series polygon,
.apexcharts-toolbar svg,
.apexcharts-tooltip .apexcharts-marker,
.apexcharts-xaxis-annotation-label,
.apexcharts-yaxis-annotation-label,
.apexcharts-zoom-rect,
.no-pointer-events {
pointer-events: none
}
.apexcharts-tooltip-active .apexcharts-marker {
transition: .15s ease all
}
.apexcharts-radar-series .apexcharts-yaxis {
pointer-events: none;
}
.resize-triggers {
animation: 1ms resizeanim;
visibility: hidden;
opacity: 0;
height: 100%;
width: 100%;
overflow: hidden
}
.contract-trigger:before,
.resize-triggers,
.resize-triggers>div {
content: " ";
display: block;
position: absolute;
top: 0;
left: 0
}
.resize-triggers>div {
height: 100%;
width: 100%;
background: #eee;
overflow: auto
}
.contract-trigger:before {
overflow: hidden;
width: 200%;
height: 200%
}
.apexcharts-bar-goals-markers {
pointer-events: none
}
.apexcharts-bar-shadows {
pointer-events: none
}
.apexcharts-rangebar-goals-markers {
pointer-events: none
}
.apexcharts-disable-transitions * {
transition: none !important;
}`;
class ApexCharts {
/**
* Creates a new ApexCharts instance.
*
* @param {HTMLElement} el - The DOM element to render the chart into.
* @param {ApexOptions} opts - Chart configuration options.
*/
constructor(el, opts) {
// Module properties set dynamically by InitCtxVariables.initModules().
// Declared as typed class fields so @ts-check resolves them throughout the
// class body without errors. Each field typed as `any` since the modules are
// plain objects whose specific shapes are not yet typed.
/** @type {any} */
__publicField(this, "core");
/** @type {any} */
__publicField(this, "responsive");
/** @type {any} */
__publicField(this, "axes");
/** @type {any} */
__publicField(this, "grid");
/** @type {any} */
__publicField(this, "graphics");
/** @type {any} */
__publicField(this, "coreUtils");
/** @type {any} */
__publicField(this, "crosshairs");
/** @type {any} */
__publicField(this, "events");
/** @type {any} */
__publicField(this, "fill");
/** @type {any} */
__publicField(this, "localization");
/** @type {any} */
__publicField(this, "options");
/** @type {any} */
__publicField(this, "series");
/** @type {any} */
__publicField(this, "theme");
/** @type {any} */
__publicField(this, "formatters");
/** @type {any} */
__publicField(this, "titleSubtitle");
/** @type {any} */
__publicField(this, "dimensions");
/** @type {any} */
__publicField(this, "updateHelpers");
/** @type {any} */
__publicField(this, "tooltip");
/** @type {any} */
__publicField(this, "data");
/** @type {any} */
__publicField(this, "animations");
/** @type {any} */
__publicField(this, "exports");
/** @type {any} */
__publicField(this, "legend");
/** @type {any} */
__publicField(this, "toolbar");
/** @type {any} */
__publicField(this, "zoomPanSelection");
/** @type {any} */
__publicField(this, "keyboardNavigation");
/** @type {any} */
__publicField(this, "annotations");
/** @type {any} */
__publicField(this, "timeScale");
/** @type {any} */
__publicField(this, "_keyboardNavigation");
/** @type {any} */
__publicField(this, "windowResizeHandler");
/** @type {any} */
__publicField(this, "parentResizeHandler");
/** @type {string[]} */
__publicField(this, "publicMethods", []);
/** @type {string[]} */
__publicField(this, "eventList", []);
/** @type {any} */
__publicField(this, "config");
this.opts = opts;
this.ctx = this;
this.w = new Base(opts).init();
this.el = el;
this.w.globals.cuid = Utils$1.randomId();
this.w.globals.chartID = this.w.config.chart.id ? Utils$1.escapeString(this.w.config.chart.id) : this.w.globals.cuid;
const initCtx = new InitCtxVariables(this);
initCtx.initModules();
this.lastUpdateOptions = null;
this.create = this.create.bind(this);
if (Environment.isBrowser()) {
this.windowResizeHandler = this._windowResizeHandler.bind(this);
this.parentResizeHandler = this._parentResizeCallback.bind(this);
}
}
/**
* Renders the chart. Must be called once after construction.
*
* @returns {Promise<ApexCharts>} Resolves with the chart instance after mount.
*/
render() {
var _a, _b;
if (!((_b = (_a = this.w) == null ? void 0 : _a.config) == null ? void 0 : _b.chart)) {
return Promise.reject(
new Error(
"ApexCharts: chart configuration is missing or invalid. Ensure the options object includes a `chart` property."
)
);
}
return new Promise((resolve, reject) => {
var _a2;
if (Utils$1.elementExists(this.el)) {
if (typeof Apex._chartInstances === "undefined") {
Apex._chartInstances = [];
}
if (this.w.config.chart.id) {
Apex._chartInstances.push({
id: this.w.globals.chartID,
group: this.w.config.chart.group,
chart: this
});
}
this.setLocale(this.w.config.chart.defaultLocale);
const beforeMount = this.w.config.chart.events.beforeMount;
if (typeof beforeMount === "function") {
beforeMount(this, this.w);
}
this.events.fireEvent("beforeMount", [this, this.w]);
if (Environment.isBrowser()) {
window.addEventListener("resize", this.windowResizeHandler);
addResizeListener(
/** @type {HTMLElement} */
this.el.parentNode,
this.parentResizeHandler
);
const rootNode = (
/** @type {any} */
this.el.getRootNode && this.el.getRootNode()
);
const inShadowRoot = Utils$1.is("ShadowRoot", rootNode);
const doc = this.el.ownerDocument;
let css = inShadowRoot ? rootNode.getElementById("apexcharts-css") : doc.getElementById("apexcharts-css");
if (!css) {
css = BrowserAPIs.createElementNS(
"http://www.w3.org/1999/xhtml",
"style"
);
css.id = "apexcharts-css";
css.textContent = apexCSS;
const nonce = ((_a2 = this.opts.chart) == null ? void 0 : _a2.nonce) || this.w.config.chart.nonce;
if (nonce) {
css.setAttribute("nonce", nonce);
}
if (inShadowRoot) {
rootNode.prepend(css);
} else if (this.w.config.chart.injectStyleSheet !== false) {
doc.head.appendChild(css);
}
}
}
const graphData = this.create(this.w.config.series, {});
if (!graphData) return resolve(this);
this.mount(graphData).then(() => {
if (typeof this.w.config.chart.events.mounted === "function") {
this.w.config.chart.events.mounted(this, this.w);
}
this.events.fireEvent("mounted", [this, this.w]);
resolve(graphData);
}).catch((e) => {
var _a3, _b2;
const enriched = e instanceof Error ? e : new Error(String(e));
const err = (
/** @type {any} */
enriched
);
err.chartId = (_b2 = (_a3 = this.w) == null ? void 0 : _a3.globals) == null ? void 0 : _b2.chartID;
err.el = this.el;
reject(enriched);
});
} else {
reject(new Error("Element not found"));
}
});
}
/**
* @param {any[]} ser
* @param {object} opts
*/
create(ser, opts) {
var _a;
const w = this.w;
if (!this.core) {
const initCtx = new InitCtxVariables(this);
initCtx.initModules();
}
const gl = this.w.globals;
gl.noData = false;
gl.animationEnded = false;
if (!Utils$1.elementExists(this.el)) {
gl.animationEnded = true;
return null;
}
this.responsive.checkResponsiveConfig(opts);
if (w.config.xaxis.convertedCatToNumeric) {
const defaults = new Defaults(w.config);
defaults.convertCatToNumericXaxis(w.config, this.ctx);
}
this.core.setupElements();
if (w.config.chart.type === "treemap") {
w.config.grid.show = false;
w.config.yaxis[0].show = false;
}
if (gl.svgWidth === 0) {
gl.animationEnded = true;
return null;
}
let series = ser;
ser.forEach((s, realIndex) => {
if (s.hidden) {
series = this.legend.legendHelpers.getSeriesAfterCollapsing({
realIndex
});
}
});
const combo = CoreUtils.checkComboSeries(series, w.config.chart.type);
gl.comboCharts = combo.comboCharts;
gl.comboBarCount = combo.comboBarCount;
const allSeriesAreEmpty = series.every((s) => s.data && s.data.length === 0);
if (series.length === 0 || allSeriesAreEmpty && gl.collapsedSeries.length < 1) {
this.series.handleNoData();
}
if (Environment.isBrowser()) {
this.events.setupEventHandlers();
}
const parsedState = this.data.parseData(series);
this._writeParsedSeriesData(parsedState.seriesData);
this._writeParsedRangeData(parsedState.rangeData);
this._writeParsedCandleData(parsedState.candleData);
this._writeParsedLabelData(parsedState.labelData);
this._writeParsedAxisFlags(parsedState.axisFlags);
this.theme.init();
const markers = new Markers(this.w, this);
markers.setGlobalMarkerSize();
this.formatters.setLabelFormatters();
this.titleSubtitle.draw();
if (!gl.noData || gl.collapsedSeries.length === w.seriesData.series.length || w.config.legend.showForSingleSeries) {
(_a = this.legend) == null ? void 0 : _a.init();
}
this.series.hasAllSeriesEqualX();
if (gl.axisCharts) {
this.core.coreCalculations();
if (w.config.xaxis.type !== "category") {
this.formatters.setLabelFormatters();
}
if (this.ctx.toolbar) {
this.ctx.toolbar.minX = w.globals.minX;
this.ctx.toolbar.maxX = w.globals.maxX;
}
}
this.formatters.heatmapLabelFormatters();
const coreUtils = new CoreUtils(this.w);
coreUtils.getLargestMarkerSize();
const layoutState = this.dimensions.plotCoords();
this._writeLayoutCoords(layoutState.layout);
const xyRatios = this.core.xySettings();
this.grid.createGridMask();
const elGraph = this.core.plotChartType(series, xyRatios);
const dataLabels = new DataLabels(this.w, this);
dataLabels.bringForward();
if (w.config.dataLabels.background.enabled) {
dataLabels.dataLabelsBackground();
}
this.core.shiftGraphPosition();
if (w.globals.dataPoints > 50) {
w.dom.elWrap.classList.add("apexcharts-disable-transitions");
}
const dim = {
plot: {
left: w.layout.translateX,
top: w.layout.translateY,
width: w.layout.gridWidth,
height: w.layout.gridHeight
}
};
return {
elGraph,
xyRatios,
dimensions: dim
};
}
/**
* @param {any} graphData
*/
mount(graphData = null) {
const me = this;
const w = me.w;
return new Promise((resolve, reject) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
if (me.el === null) {
return reject(
new Error("Not enough data to display or target element not found")
);
} else if (graphData === null || w.globals.allSeriesCollapsed) {
me.series.handleNoData();
}
me.grid = new Grid(me.w, me);
const elgrid = me.grid.drawGrid();
const AnnotationsCtor = InitCtxVariables._featureRegistry.get("annotations");
me.annotations = AnnotationsCtor ? new AnnotationsCtor(me.w, {
theme: me.theme,
timeScale: me.timeScale
}) : null;
(_a = me.annotations) == null ? void 0 : _a.drawImageAnnos();
(_b = me.annotations) == null ? void 0 : _b.drawTextAnnos();
if (w.config.grid.position === "back") {
if (elgrid) {
w.dom.elGraphical.add(elgrid.el);
}
if ((_c = elgrid == null ? void 0 : elgrid.elGridBorders) == null ? void 0 : _c.node) {
w.dom.elGraphical.add(elgrid.elGridBorders);
}
}
if (Array.isArray(graphData.elGraph)) {
for (let g = 0; g < graphData.elGraph.length; g++) {
w.dom.elGraphical.add(graphData.elGraph[g]);
}
} else {
w.dom.elGraphical.add(graphData.elGraph);
}
if (w.config.grid.position === "front") {
if (elgrid) {
w.dom.elGraphical.add(elgrid.el);
}
if ((_d = elgrid == null ? void 0 : elgrid.elGridBorders) == null ? void 0 : _d.node) {
w.dom.elGraphical.add(elgrid.elGridBorders);
}
}
if (w.config.xaxis.crosshairs.position === "front") {
me.crosshairs.drawXCrosshairs();
}
if (w.config.yaxis[0].crosshairs.position === "front") {
me.crosshairs.drawYCrosshairs();
}
if (w.config.chart.type !== "treemap") {
me.axes.drawAxis(w.config.chart.type, elgrid);
}
const xAxis = new XAxis(this.w, this.ctx, elgrid);
const yaxis = new YAxis(
this.w,
{ theme: this.theme, timeScale: this.timeScale },
elgrid
);
if (elgrid !== null) {
xAxis.xAxisLabelCorrections();
yaxis.setYAxisTextAlignments();
w.config.yaxis.map((yaxe, index) => {
if (w.globals.ignoreYAxisIndexes.indexOf(index) === -1) {
yaxis.yAxisTitleRotate(index, yaxe.opposite);
}
});
}
(_e = me.annotations) == null ? void 0 : _e.drawAxesAnnotations();
if (!w.globals.noData) {
if (Environment.isBrowser() && w.config.tooltip.enabled && !w.globals.noData) {
(_f = me.w.globals.tooltip) == null ? void 0 : _f.drawTooltip(graphData.xyRatios);
}
if (w.config.chart.accessibility.enabled && w.config.chart.accessibility.keyboard.enabled && w.config.chart.accessibility.keyboard.navigation.enabled) {
(_g = me.keyboardNavigation) == null ? void 0 : _g.init();
}
if (Environment.isBrowser() && w.globals.axisCharts && (w.axisFlags.isXNumeric || /** @type {Record<string,any>} */
w.config.xaxis.convertedCatToNumeric || w.axisFlags.isRangeBar)) {
if (w.config.chart.zoom.enabled || w.config.chart.selection && w.config.chart.selection.enabled || // @ts-ignore — chart.pan is an internal toolbar config property
w.config.chart.pan && w.config.chart.pan.enabled) {
(_h = me.zoomPanSelection) == null ? void 0 : _h.init({
xyRatios: graphData.xyRatios
});
}
} else {
const tools = w.config.chart.toolbar.tools;
const toolsArr = [
"zoom",
"zoomin",
"zoomout",
"selection",
"pan",
"reset"
];
toolsArr.forEach((t) => {
tools[t] = false;
});
}
if (w.config.chart.toolbar.show && !w.globals.allSeriesCollapsed) {
(_i = me.toolbar) == null ? void 0 : _i.createToolbar();
}
}
if (w.globals.memory.methodsToExec.length > 0) {
w.globals.memory.methodsToExec.forEach((fn) => {
fn.method(fn.params, false, fn.context);
});
}
if (!w.globals.axisCharts && !w.globals.noData) {
me.core.resizeNonAxisCharts();
}
resolve(me);
});
}
/**
* Destroys the chart instance, removes all DOM elements and event listeners.
* After calling this, the instance should not be used again.
*/
destroy() {
if (Environment.isBrowser()) {
window.removeEventListener("resize", this.windowResizeHandler);
removeResizeListener(
/** @type {Element} */
this.el.parentNode,
this.parentResizeHandler
);
}
const chartID = this.w.config.chart.id;
if (chartID) {
Apex._chartInstances.forEach(
(c, i) => {
if (c.id === Utils$1.escapeString(chartID)) {
Apex._chartInstances.splice(i, 1);
}
}
);
}
if (this._keyboardNavigation) {
this._keyboardNavigation.destroy();
}
new Destroy(this.ctx).clear({ isUpdating: false });
}
/**
* Merges new options into the existing config and re-renders the chart.
*
* @param {ApexOptions} options - Partial config object merged with the existing config.
* @param {boolean} [redraw=false] - When true, redraws the chart from scratch instead of animating from previous paths.
* @param {boolean} [animate=true] - Whether to animate the update.
* @param {boolean} [updateSyncedCharts=true] - Whether to propagate the update to charts in the same group.
* @param {boolean} [overwriteInitialConfig=true] - When true, replaces the stored initial config used by resetSeries().
* @returns {Promise<ApexCharts>} Resolves with the chart instance after re-render.
*/
updateOptions(options2, redraw = false, animate = true, updateSyncedCharts = true, overwriteInitialConfig = true) {
const w = this.w;
w.interact.selection = void 0;
if (this.lastUpdateOptions) {
if (Utils$1.shallowEqual(this.lastUpdateOptions, options2)) {
return Promise.resolve(this);
}
if (options2.series && this.lastUpdateOptions.series) {
if (JSON.stringify(this.lastUpdateOptions.series) === JSON.stringify(options2.series)) {
const optionsWithoutSeries = __spreadValues({}, options2);
const lastWithoutSeries = __spreadValues({}, this.lastUpdateOptions);
delete optionsWithoutSeries.series;
delete lastWithoutSeries.series;
if (Utils$1.shallowEqual(optionsWithoutSeries, lastWithoutSeries)) {
return Promise.resolve(this);
}
}
}
}
if (options2.series) {
this.data.resetParsingFlags();
this.series.resetSeries(false, true, false);
if (options2.series.length && options2.series[0].data) {
options2.series = options2.series.map(
(s, i) => {
return this.updateHelpers._extendSeries(s, i);
}
);
}
this.updateHelpers.revertDefaultAxisMinMax();
}
if (options2.xaxis) {
options2 = this.updateHelpers.forceXAxisUpdate(options2);
}
if (options2.yaxis) {
options2 = this.updateHelpers.forceYAxisUpdate(options2);
}
if (w.globals.collapsedSeriesIndices.length > 0) {
this.series.clearPreviousPaths();
}
if (options2.theme) {
options2 = this.theme.updateThemeOptions(options2);
}
return this.updateHelpers._updateOptions(
options2,
redraw,
animate,
updateSyncedCharts,
overwriteInitialConfig
);
}
/**
* Replaces the chart's series data and re-renders.
*
* @param {ApexAxisChartSeries | ApexNonAxisChartSeries} [newSeries=[]] - The replacement series array.
* @param {boolean} [animate=true] - Whether to animate the update.
* @param {boolean} [overwriteInitialSeries=true] - When true, replaces the stored initial series used by resetSeries().
* @returns {Promise<ApexCharts>} Resolves with the chart instance after re-render.
*/
updateSeries(newSeries = [], animate = true, overwriteInitialSeries = true) {
this.data.resetParsingFlags();
this.series.resetSeries(false);
this.updateHelpers.revertDefaultAxisMinMax();
return this.updateHelpers._updateSeries(
newSeries,
animate,
overwriteInitialSeries
);
}
/**
* Appends a new series to the existing series array and re-renders.
*
* @param {ApexAxisChartSeries[0] | ApexNonAxisChartSeries} newSerie - The series object to append.
* @param {boolean} [animate=true] - Whether to animate the update.
* @param {boolean} [overwriteInitialSeries=true] - When true, replaces the stored initial series used by resetSeries().
* @returns {Promise<ApexCharts>} Resolves with the chart instance after re-render.
*/
appendSeries(newSerie, animate = true, overwriteInitialSeries = true) {
this.data.resetParsingFlags();
const newSeries = this.w.config.series.slice();
newSeries.push(
/** @type {any} */
newSerie
);
this.series.resetSeries(false);
this.updateHelpers.revertDefaultAxisMinMax();
return this.updateHelpers._updateSeries(
newSeries,
animate,
overwriteInitialSeries
);
}
/**
* Appends data points to existing series without replacing them.
* Each element of `newData` corresponds to the series at the same index.
*
* @param {Array<{ data: any[] }>} newData - Data to append, in the same shape as series[].data.
* @param {boolean} [overwriteInitialSeries=true] - When true, updates the stored initial series used by resetSeries().
* @returns {Promise<ApexCharts>} Resolves with the chart instance after re-render.
*/
appendData(newData, overwriteInitialSeries = true) {
const me = this;
me.data.resetParsingFlags();
me.w.globals.dataChanged = true;
me.series.getPreviousPaths();
const newSeries = me.w.config.series.slice();
for (let i = 0; i < newSeries.length; i++) {
if (newData[i] !== null && typeof newData[i] !== "undefined") {
const srcSerie = (
/** @type {any} */
newData[i]
);
const dstSerie = (
/** @type {any} */
newSeries[i]
);
for (let j = 0; j < srcSerie.data.length; j++) {
dstSerie.data.push(srcSerie.data[j]);
}
}
}
me.w.config.series = newSeries;
if (overwriteInitialSeries) {
me.w.globals.initialSeries = Utils$1.clone(me.w.config.series);
}
return this.update();
}
/**
* @param {object} [options]
*/
update(options2) {
return new Promise((resolve, reject) => {
if (this.lastUpdateOptions && JSON.stringify(this.lastUpdateOptions) === JSON.stringify(options2)) {
return resolve(this);
}
this.lastUpdateOptions = Utils$1.clone(options2);
new Destroy(this.ctx).clear({ isUpdating: true });
const graphData = this.create(this.w.config.series, options2 != null ? options2 : {});
if (!graphData) return resolve(this);
this.mount(graphData).then(() => {
if (typeof this.w.config.chart.events.updated === "function") {
this.w.config.chart.events.updated(this, this.w);
}
this.events.fireEvent("updated", [this, this.w]);
this.w.globals.isDirty = true;
resolve(this);
}).catch((e) => {
reject(e);
});
});
}
/**
* Fast update path for data-only series changes.
*
* Skips rebuilding grid, axes, dimensions, legend, annotations, tooltip DOM,
* and toolbar. Only recalculates scales and replots the series paths.
* Called automatically by _updateSeries() when the fast path is eligible.
*
* @param {boolean} animate - Whether to animate the update.
* @returns {Promise<ApexCharts>} Resolves with the chart instance.
*/
fastUpdate(animate) {
return new Promise((resolve, reject) => {
var _a;
try {
const w = this.w;
const gl = w.globals;
gl.shouldAnimate = animate;
gl.dataChanged = true;
gl.animationEnded = false;
PerformanceCache.invalidateSelectors(w);
const gl2 = w.globals;
gl2.maxY = -Number.MAX_VALUE;
gl2.minY = Number.MIN_VALUE;
gl2.minYArr = [];
gl2.maxYArr = [];
gl2.maxX = -Number.MAX_VALUE;
gl2.minX = Number.MAX_VALUE;
gl2.initialMaxX = -Number.MAX_VALUE;
gl2.initialMinX = Number.MAX_VALUE;
gl2.yAxisScale = [];
gl2.xAxisScale = null;
gl2.xAxisTicksPositions = [];
gl2.xRange = 0;
gl2.yRange = [];
gl2.zRange = 0;
gl2.xTickAmount = 0;
gl2.multiAxisTickAmount = 0;
gl2.pointsArray = [];
gl2.dataLabelsRects = [];
gl2.lastDrawnDataLabelsIndexes = [];
gl2.textRectsCache = /* @__PURE__ */ new Map();
gl2.domCache = /* @__PURE__ */ new Map();
gl2.cachedSelectors = {};
gl2.disableZoomIn = false;
gl2.disableZoomOut = false;
if (gl.axisCharts) {
this.core.coreCalculations();
if (w.config.xaxis.type !== "category") {
this.formatters.setLabelFormatters();
}
}
const xyRatios = this.core.xySettings();
const innerEl = w.dom.elGraphical.node;
const toRemove = innerEl.querySelectorAll(
".apexcharts-series, .apexcharts-datalabels, .apexcharts-datalabels-background"
);
toRemove.forEach(
(el) => {
var _a2;
return (_a2 = el.parentNode) == null ? void 0 : _a2.removeChild(el);
}
);
const elGraph = this.core.plotChartType(w.config.series, xyRatios);
const gridEl = innerEl.querySelector(".apexcharts-grid");
const graphs = Array.isArray(elGraph) ? elGraph : [elGraph];
if (gridEl && w.config.grid.position === "front") {
graphs.forEach((g) => {
const node = g && g.node ? g.node : g;
if (node) innerEl.insertBefore(node, gridEl);
});
} else {
graphs.forEach((g) => {
w.dom.elGraphical.add(g);
});
}
const dataLabels = new DataLabels(w, this);
dataLabels.bringForward();
if (w.config.dataLabels.background.enabled) {
dataLabels.dataLabelsBackground();
}
if (Environment.isBrowser() && w.config.tooltip.enabled && !gl.noData) {
(_a = w.globals.tooltip) == null ? void 0 : _a.drawTooltip(xyRatios);
}
if (typeof w.config.chart.events.updated === "function") {
w.config.chart.events.updated(this, w);
}
this.events.fireEvent("updated", [this, w]);
gl.isDirty = true;
resolve(this);
} catch (e) {
reject(e);
}
});
}
/**
* Returns all charts in the same `chart.group` (including this instance),
* used to synchronise zoom/pan across grouped charts.
*
* @returns {ApexCharts[]}
*/
getSyncedCharts() {
const chartGroups = this.getGroupedCharts();
let allCharts = (
/** @type {ApexCharts[]} */
[this]
);
if (chartGroups.length) {
allCharts = [];
chartGroups.forEach((ch) => {
allCharts.push(ch);
});
}
return allCharts;
}
/**
* Returns all charts in the same `chart.group`, excluding this instance.
* Used internally to apply hover/zoom effects to sibling charts.
*
* @returns {ApexCharts[]}
*/
getGroupedCharts() {
return Apex._chartInstances.filter((ch) => {
if (ch.group) {
return true;
}
}).map(
(ch) => this.w.config.chart.group === ch.group ? ch.chart : this
);
}
/**
* Retrieves a rendered chart instance by its `chart.id` config value.
*
* @param {string} id - The chart ID set via `chart.id` in options.
* @returns {ApexCharts | undefined}
*/
static getChartByID(id) {
const chartId = Utils$1.escapeString(id);
if (!Apex._chartInstances) return void 0;
const c = Apex._chartInstances.filter(
(ch) => ch.id === chartId
)[0];
return c && c.chart;
}
/**
* Scans the document for elements with a `data-apexcharts` attribute and
* `data-options` JSON, then renders a chart in each one automatically.
* Useful for non-framework HTML pages.
*/
static initOnLoad() {
var _a;
const els = document.querySelectorAll("[data-apexcharts]");
for (let i = 0; i < els.length; i++) {
const el = (
/** @type {HTMLElement} */
els[i]
);
const options2 = JSON.parse((_a = els[i].getAttribute("data-options")) != null ? _a : "");
const apexChart = new ApexCharts(el, options2);
apexChart.render();
}
}
/**
* This static method allows users to call chart methods without necessarily from the
* instance of the chart in case user has assigned chartID to the targeted chart.
* The chartID is used for mapping the instance stored in Apex._chartInstances global variable
*
* This is helpful in cases when you don't have reference of the chart instance
* easily and need to call the method from anywhere.
* For eg, in React/Vue applications when you have many parent/child components,
* and need easy reference to other charts for performing dynamic operations
*
* @param {string} chartID - The unique identifier which will be used to call methods
* on that chart instance
* @param {string} fn - The method name to call
* @param {...any} opts - The parameters which are accepted in the original method will be passed here in the same order.
*/
static exec(chartID, fn, ...opts) {
const chart = this.getChartByID(chartID);
if (!chart) return;
chart.w.globals.isExecCalled = true;
let ret = null;
if (chart.publicMethods.indexOf(fn) !== -1) {
ret = /** @type {any} */
chart[fn](...opts);
}
return ret;
}
/**
* Deep-merges `source` into `target` and returns the result.
* Thin wrapper around the internal `Utils.extend` utility.
*
* @param {object} target
* @param {object} source
* @returns {object}
*/
static merge(target, source) {
return Utils$1.extend(target, source);
}
static getThemePalettes() {
return getThemePalettes();
}
/**
* Register additional chart types. Used by sub-entry points so that only
* the types they include are bundled.
*
* @param {Record<string, new (...args: any[]) => any>} typeMap e.g. { line: Line, area: Line }
*/
static use(typeMap) {
register(typeMap);
}
/**
* Register optional feature modules (Exports, Legend, Toolbar,
* ZoomPanSelection, KeyboardNavigation, Annotations).
*
* Call this before rendering any chart. Feature entry files (e.g.
* `apexcharts/features/legend`) call this automatically when imported.
* Note: Tooltip is part of core and does not need to be registered.
*
* @param {Record<string, new (...args: any[]) => any>} featureMap e.g. { legend: Legend, exports: Exports }
*/
static registerFeatures(featureMap) {
InitCtxVariables.registerFeatures(featureMap);
}
/**
* Toggles (show/hide) the series identified by name.
* Mirrors a click on the corresponding legend item.
*
* @param {string} seriesName
* @returns {object | undefined} The collapsed series object, if now hidden.
*/
toggleSeries(seriesName) {
return this.series.toggleSeries(seriesName);
}
/**
* Highlights or un-highlights a series when the user hovers a legend item.
* Called internally by the legend; not typically called by consumers.
*
* @param {MouseEvent} e
* @param {HTMLElement} targetElement - The legend marker element being hovered.
*/
highlightSeriesOnLegendHover(e, targetElement) {
return this.series.toggleSeriesOnHover(e, targetElement);
}
/**
* Makes a previously hidden series visible and re-renders.
*
* @param {string} seriesName
*/
showSeries(seriesName) {
this.series.showSeries(seriesName);
}
/**
* Hides a visible series and re-renders.
*
* @param {string} seriesName
*/
hideSeries(seriesName) {
this.series.hideSeries(seriesName);
}
/**
* Highlights (dims all other series) the series identified by name.
*
* @param {string} seriesName
*/
highlightSeries(seriesName) {
this.series.highlightSeries(seriesName);
}
/**
* Returns whether the series identified by name is currently hidden.
*
* @param {string} seriesName
* @returns {boolean}
*/
isSeriesHidden(seriesName) {
return this.series.isSeriesHidden(seriesName);
}
/**
* Resets the chart to the initial series and optionally the initial zoom level.
*
* @param {boolean} [shouldUpdateChart=true] - When true, triggers a re-render.
* @param {boolean} [shouldResetZoom=true] - When true, restores the initial zoom level.
*/
resetSeries(shouldUpdateChart = true, shouldResetZoom = true) {
this.series.resetSeries(shouldUpdateChart, shouldResetZoom);
}
/**
* Subscribes to a chart event by name.
* Supported event names mirror the `chart.events` option keys
* (e.g. `'mounted'`, `'updated'`, `'dataPointMouseEnter'`).
*
* @param {string} name - Event name.
* @param {Function} handler - Callback invoked when the event fires.
*/
addEventListener(name2, handler) {
this.events.addEventListener(name2, handler);
}
/**
* Removes a previously registered event listener.
*
* @param {string} name - Event name.
* @param {Function} handler - The exact function reference passed to addEventListener.
*/
removeEventListener(name2, handler) {
this.events.removeEventListener(name2, handler);
}
/**
* Adds an x-axis annotation dynamically after render.
*
* @param {XAxisAnnotations} opts - Annotation configuration.
* @param {boolean} [pushToMemory=true] - When true, the annotation persists across re-renders.
* @param {ApexCharts} [context] - Override the target chart instance (used by exec()).
*/
addXaxisAnnotation(opts, pushToMemory = true, context = void 0) {
var _a;
let me = (
/** @type {ApexCharts} */
/** @type {unknown} */
this
);
if (context) {
me = context;
}
(_a = me.annotations) == null ? void 0 : _a.addXaxisAnnotationExternal(opts, pushToMemory, me);
}
/**
* Adds a y-axis annotation dynamically after render.
*
* @param {YAxisAnnotations} opts - Annotation configuration.
* @param {boolean} [pushToMemory=true] - When true, the annotation persists across re-renders.
* @param {ApexCharts} [context] - Override the target chart instance (used by exec()).
*/
addYaxisAnnotation(opts, pushToMemory = true, context = void 0) {
var _a;
let me = (
/** @type {ApexCharts} */
/** @type {unknown} */
this
);
if (context) {
me = context;
}
(_a = me.annotations) == null ? void 0 : _a.addYaxisAnnotationExternal(opts, pushToMemory, me);
}
/**
* Adds a point annotation dynamically after render.
*
* @param {PointAnnotations} opts - Annotation configuration.
* @param {boolean} [pushToMemory=true] - When true, the annotation persists across re-renders.
* @param {ApexCharts} [context] - Override the target chart instance (used by exec()).
*/
addPointAnnotation(opts, pushToMemory = true, context = void 0) {
var _a;
let me = (
/** @type {ApexCharts} */
/** @type {unknown} */
this
);
if (context) {
me = context;
}
(_a = me.annotations) == null ? void 0 : _a.addPointAnnotationExternal(opts, pushToMemory, me);
}
/**
* Removes all annotations from the chart.
*
* @param {ApexCharts} [context] - Override the target chart instance (used by exec()).
*/
clearAnnotations(context = void 0) {
var _a;
let me = (
/** @type {ApexCharts} */
/** @type {unknown} */
this
);
if (context) {
me = context;
}
(_a = me.annotations) == null ? void 0 : _a.clearAnnotations(me);
}
/**
* Removes a specific annotation by its `id`.
*
* @param {string} id - The annotation id as set in the annotation config.
* @param {ApexCharts} [context] - Override the target chart instance (used by exec()).
*/
removeAnnotation(id, context = void 0) {
var _a;
let me = (
/** @type {ApexCharts} */
/** @type {unknown} */
this
);
if (context) {
me = context;
}
(_a = me.annotations) == null ? void 0 : _a.removeAnnotation(me, id);
}
/**
* Returns the inner SVG group element that contains all chart graphics.
*
* @returns {Element | null}
*/
getChartArea() {
const el = this.w.dom.baseEl.querySelector(".apexcharts-inner");
return el;
}
/**
* Returns the sum of all data points whose x value falls within [minX, maxX].
*
* @param {number} minX
* @param {number} maxX
* @returns {number[]} One total per series.
*/
getSeriesTotalXRange(minX, maxX) {
return this.coreUtils.getSeriesTotalsXRange(minX, maxX);
}
/**
* Returns the highest y value in the specified series.
*
* @param {number} [seriesIndex=0]
* @returns {number}
*/
getHighestValueInSeries(seriesIndex = 0) {
const range = new Range(this.w);
return range.getMinYMaxY(seriesIndex).highestY;
}
/**
* Returns the lowest y value in the specified series.
*
* @param {number} [seriesIndex=0]
* @returns {number}
*/
getLowestValueInSeries(seriesIndex = 0) {
const range = new Range(this.w);
return range.getMinYMaxY(seriesIndex).lowestY;
}
/**
* Returns the sum of each series (the totals used for percentage calculations).
*
* @returns {number[]}
*/
getSeriesTotal() {
return this.w.globals.seriesTotals;
}
/**
* Returns a curated snapshot of chart state for use in formatters, events,
* and external integrations. Prefer this over accessing `chart.w` directly.
*
* The shape of this object is stable and versioned. `chart.w` is internal
* and will be restricted in a future major version.
*/
getState() {
const w = this.w;
const gl = w.globals;
return {
// Series data — computed/parsed form used for rendering
series: w.seriesData.series,
seriesNames: w.seriesData.seriesNames,
colors: gl.colors,
labels: w.labelData.labels,
seriesTotals: gl.seriesTotals,
seriesPercent: gl.seriesPercent,
seriesXvalues: gl.seriesXvalues,
seriesYvalues: gl.seriesYvalues,
// Axis bounds — updated after each render
minX: gl.minX,
maxX: gl.maxX,
minY: gl.minY,
maxY: gl.maxY,
minYArr: gl.minYArr,
maxYArr: gl.maxYArr,
minXDiff: gl.minXDiff,
dataPoints: gl.dataPoints,
// Axis scale objects — computed tick/scale results
xAxisScale: gl.xAxisScale,
yAxisScale: gl.yAxisScale,
xTickAmount: gl.xTickAmount,
// Axis type flags
isXNumeric: w.axisFlags.isXNumeric,
// Multi-axis series mapping
seriesYAxisMap: gl.seriesYAxisMap,
seriesYAxisReverseMap: gl.seriesYAxisReverseMap,
// Chart dimensions — updated after each render/resize
svgWidth: gl.svgWidth,
svgHeight: gl.svgHeight,
gridWidth: w.layout.gridWidth,
gridHeight: w.layout.gridHeight,
// Interactive state
selectedDataPoints: w.interact.selectedDataPoints,
collapsedSeriesIndices: gl.collapsedSeriesIndices,
zoomed: w.interact.zoomed,
// Chart-type-specific series data (null when not applicable)
seriesX: w.seriesData.seriesX,
seriesZ: w.seriesData.seriesZ,
seriesCandleO: w.candleData.seriesCandleO,
seriesCandleH: w.candleData.seriesCandleH,
seriesCandleM: w.candleData.seriesCandleM,
seriesCandleL: w.candleData.seriesCandleL,
seriesCandleC: w.candleData.seriesCandleC,
seriesRangeStart: w.rangeData.seriesRangeStart,
seriesRangeEnd: w.rangeData.seriesRangeEnd,
seriesGoals: w.seriesData.seriesGoals
};
}
/**
* Programmatically selects or deselects a data point.
* Equivalent to a user click on the data point.
*
* @param {number} seriesIndex - Zero-based series index.
* @param {number} [dataPointIndex] - Zero-based data point index within the series.
* @returns {number[][] | null} Updated selectedDataPoints array, or null.
*/
toggleDataPointSelection(seriesIndex, dataPointIndex) {
return this.updateHelpers.toggleDataPointSelection(
seriesIndex,
dataPointIndex
);
}
/**
* Programmatically zooms the x-axis to the given range.
* Requires zoom to be enabled (`chart.zoom.enabled: true`).
*
* @param {number} min - The minimum x value (timestamp or numeric).
* @param {number} max - The maximum x value (timestamp or numeric).
*/
zoomX(min, max) {
var _a;
(_a = this.ctx.toolbar) == null ? void 0 : _a.zoomUpdateOptions(min, max);
}
/**
* Switches the active locale, updating all locale-dependent labels (toolbar tooltips, month names, etc.).
*
* @param {string} localeName - Must match a locale name defined in `chart.locales`.
*/
setLocale(localeName) {
this.localization.setCurrentLocaleValues(localeName);
}
/**
* Exports the chart to a PNG or SVG data URI.
* Requires the Exports feature: `import 'apexcharts/features/exports'`.
*
* @param {{ scale?: number, width?: number }} [options]
* @returns {Promise<{ imgURI: string } | { blob: Blob }>}
*/
dataURI(options2) {
if (!this.ctx.exports)
throw new Error(
"apexcharts: Exports feature is not registered. Import apexcharts/features/exports."
);
return this.ctx.exports.dataURI(options2);
}
/**
* Returns the chart's SVG markup as a string, optionally scaled.
* Requires the Exports feature: `import 'apexcharts/features/exports'`.
*
* @param {number} [scale=1]
* @returns {Promise<string>}
*/
getSvgString(scale) {
if (!this.ctx.exports)
throw new Error(
"apexcharts: Exports feature is not registered. Import apexcharts/features/exports."
);
return this.ctx.exports.getSvgString(scale);
}
/**
* Triggers a CSV download of the chart's data.
* Requires the Exports feature: `import 'apexcharts/features/exports'`.
*
* @param {{ series?: any, fileName?: string, columnDelimiter?: string, lineDelimiter?: string }} [options]
*/
exportToCSV(options2 = {}) {
if (!this.ctx.exports)
throw new Error(
"apexcharts: Exports feature is not registered. Import apexcharts/features/exports."
);
return this.ctx.exports.exportToCSV(options2);
}
paper() {
return this.w.dom.Paper;
}
// ─── Slice write-back stubs ─────────────────────────────────────────────────
/**
* @param {Partial<import('./types/internal').SeriesData>} slice
*/
_writeParsedSeriesData(slice) {
Object.assign(this.w.seriesData, slice);
}
/**
* @param {Partial<import('./types/internal').RangeData>} slice
*/
_writeParsedRangeData(slice) {
Object.assign(this.w.rangeData, slice);
}
/**
* @param {Partial<import('./types/internal').CandleData>} slice
*/
_writeParsedCandleData(slice) {
Object.assign(this.w.candleData, slice);
}
/**
* @param {Partial<import('./types/internal').LabelData>} slice
*/
_writeParsedLabelData(slice) {
Object.assign(this.w.labelData, slice);
}
/**
* @param {Partial<import('./types/internal').AxisFlags>} slice
*/
_writeParsedAxisFlags(slice) {
Object.assign(this.w.axisFlags, slice);
}
/**
* @param {Partial<import('./types/internal').LayoutCoords>} slice
*/
_writeLayoutCoords(slice) {
Object.assign(this.w.layout, slice);
}
_parentResizeCallback() {
if (this.w.globals.animationEnded && this.w.config.chart.redrawOnParentResize) {
this._windowResize();
}
}
/**
* Handle window resize and re-draw the whole chart.
*/
_windowResize() {
this.w.globals.resizeTimer = window.setTimeout(() => {
this.w.globals.resized = true;
this.w.globals.dataChanged = false;
this.ctx.update();
}, 150);
}
_windowResizeHandler() {
var _a;
clearTimeout((_a = this.w.globals.resizeTimer) != null ? _a : void 0);
let { redrawOnWindowResize: redraw } = this.w.config.chart;
if (typeof redraw === "function") {
redraw = /** @type {any} */
redraw();
}
redraw && this._windowResize();
}
}
const apexchartsLegendCSS = ".apexcharts-flip-y {\n transform: scaleY(-1) translateY(-100%);\n transform-origin: top;\n transform-box: fill-box;\n}\n.apexcharts-flip-x {\n transform: scaleX(-1);\n transform-origin: center;\n transform-box: fill-box;\n}\n.apexcharts-legend {\n display: flex;\n overflow: auto;\n padding: 0 10px;\n}\n.apexcharts-legend.apexcharts-legend-group-horizontal {\n flex-direction: column;\n}\n.apexcharts-legend-group {\n display: flex;\n}\n.apexcharts-legend-group-vertical {\n flex-direction: column-reverse;\n}\n.apexcharts-legend.apx-legend-position-bottom, .apexcharts-legend.apx-legend-position-top {\n flex-wrap: wrap\n}\n.apexcharts-legend.apx-legend-position-right, .apexcharts-legend.apx-legend-position-left {\n flex-direction: column;\n bottom: 0;\n}\n.apexcharts-legend.apx-legend-position-bottom.apexcharts-align-left, .apexcharts-legend.apx-legend-position-top.apexcharts-align-left, .apexcharts-legend.apx-legend-position-right, .apexcharts-legend.apx-legend-position-left {\n justify-content: flex-start;\n align-items: flex-start;\n}\n.apexcharts-legend.apx-legend-position-bottom.apexcharts-align-center, .apexcharts-legend.apx-legend-position-top.apexcharts-align-center {\n justify-content: center;\n align-items: center;\n}\n.apexcharts-legend.apx-legend-position-bottom.apexcharts-align-right, .apexcharts-legend.apx-legend-position-top.apexcharts-align-right {\n justify-content: flex-end;\n align-items: flex-end;\n}\n.apexcharts-legend-series {\n cursor: pointer;\n line-height: normal;\n display: flex;\n align-items: center;\n}\n.apexcharts-legend-text {\n position: relative;\n font-size: 14px;\n}\n.apexcharts-legend-text *, .apexcharts-legend-marker * {\n pointer-events: none;\n}\n.apexcharts-legend-marker {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n margin-right: 1px;\n}\n\n.apexcharts-legend-series.apexcharts-no-click {\n cursor: auto;\n}\n.apexcharts-legend .apexcharts-hidden-zero-series, .apexcharts-legend .apexcharts-hidden-null-series {\n display: none !important;\n}\n.apexcharts-inactive-legend {\n opacity: 0.45;\n} ";
class Exports {
/**
* @param {import('../types/internal').ChartStateW} w
* @param {import('../types/internal').ChartContext} ctx
*/
constructor(w, ctx) {
this.w = w;
this.ctx = ctx;
}
/**
* @param {string} svgString
*/
svgStringToNode(svgString) {
const parser = new DOMParser();
const svgDoc = parser.parseFromString(svgString, "image/svg+xml");
return svgDoc.documentElement;
}
/**
* @param {any} svg
* @param {number} scale
*/
scaleSvgNode(svg, scale) {
const svgWidth = parseFloat(svg.getAttributeNS(null, "width"));
const svgHeight = parseFloat(svg.getAttributeNS(null, "height"));
svg.setAttributeNS(null, "width", svgWidth * scale);
svg.setAttributeNS(null, "height", svgHeight * scale);
svg.setAttributeNS(null, "viewBox", "0 0 " + svgWidth + " " + svgHeight);
}
/**
* @param {number} [_scale]
*/
getSvgString(_scale) {
return new Promise((resolve) => {
const w = this.w;
let scale = _scale || w.config.chart.toolbar.export.scale || w.config.chart.toolbar.export.width / w.globals.svgWidth;
if (!scale) {
scale = 1;
}
const width = w.globals.svgWidth * scale;
const height = w.globals.svgHeight * scale;
const clonedNode = (
/** @type {HTMLElement} */
w.dom.elWrap.cloneNode(true)
);
clonedNode.style.width = width + "px";
clonedNode.style.height = height + "px";
const serializedNode = new XMLSerializer().serializeToString(clonedNode);
const shouldIncludeLegendStyles = w.config.legend.show && w.dom.elLegendWrap && w.dom.elLegendWrap.children.length > 0;
let exportStyles = `
.apexcharts-tooltip, .apexcharts-toolbar, .apexcharts-xaxistooltip, .apexcharts-yaxistooltip, .apexcharts-xcrosshairs, .apexcharts-ycrosshairs, .apexcharts-zoom-rect, .apexcharts-selection-rect {
display: none;
}
`;
if (shouldIncludeLegendStyles) {
exportStyles += apexchartsLegendCSS;
}
let svgString = `
<svg xmlns="http://www.w3.org/2000/svg"
version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink"
class="apexcharts-svg"
xmlns:data="ApexChartsNS"
transform="translate(0, 0)"
width="${w.globals.svgWidth}px" height="${w.globals.svgHeight}px">
<foreignObject width="100%" height="100%">
<div xmlns="http://www.w3.org/1999/xhtml" style="width:${width}px; height:${height}px;">
<style type="text/css">
${exportStyles}
</style>
${serializedNode}
</div>
</foreignObject>
</svg>
`;
const svgNode = this.svgStringToNode(svgString);
if (scale !== 1) {
this.scaleSvgNode(svgNode, scale);
}
this.convertImagesToBase64(svgNode).then(() => {
svgString = new XMLSerializer().serializeToString(svgNode);
resolve(svgString.replace(/ /g, " "));
});
});
}
/**
* @param {any} svgNode
*/
convertImagesToBase64(svgNode) {
const images = svgNode.getElementsByTagName("image");
const promises = Array.from(images).map((img) => {
const href = img.getAttributeNS("http://www.w3.org/1999/xlink", "href");
if (href && !href.startsWith("data:")) {
return this.getBase64FromUrl(href).then((base64) => {
img.setAttributeNS("http://www.w3.org/1999/xlink", "href", base64);
}).catch((error) => {
console.error("Error converting image to base64:", error);
});
}
return Promise.resolve();
});
return Promise.all(promises);
}
/**
* @param {string} url
*/
getBase64FromUrl(url) {
if (Environment.isSSR()) return Promise.resolve(url);
return new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = "Anonymous";
img.onload = () => {
const canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext("2d");
if (ctx) ctx.drawImage(img, 0, 0);
resolve(canvas.toDataURL());
};
img.onerror = reject;
img.src = url;
});
}
svgUrl() {
return new Promise((resolve) => {
this.getSvgString().then((svgData) => {
const svgBlob = new Blob([svgData], {
type: "image/svg+xml;charset=utf-8"
});
resolve(URL.createObjectURL(svgBlob));
});
});
}
/**
* @param {Record<string, any> | undefined} options
*/
dataURI(options2) {
if (Environment.isSSR()) return Promise.resolve({ imgURI: "" });
return new Promise((resolve) => {
const w = this.w;
const scale = options2 ? options2.scale || options2.width / w.globals.svgWidth : 1;
const canvas = document.createElement("canvas");
canvas.width = w.globals.svgWidth * scale;
canvas.height = parseInt(w.dom.elWrap.style.height, 10) * scale;
const canvasBg = w.config.chart.background === "transparent" || !w.config.chart.background ? "#fff" : w.config.chart.background;
const ctx = canvas.getContext("2d");
if (!ctx) return;
ctx.fillStyle = canvasBg;
ctx.fillRect(0, 0, canvas.width * scale, canvas.height * scale);
this.getSvgString(scale).then((svgData) => {
const svgUrl = "data:image/svg+xml," + encodeURIComponent(svgData);
const img = new Image();
img.crossOrigin = "anonymous";
img.onload = () => {
ctx.drawImage(img, 0, 0);
const edgeCanvas = canvas;
if (edgeCanvas.msToBlob) {
const blob = edgeCanvas.msToBlob();
resolve({ blob });
} else {
const imgURI = canvas.toDataURL("image/png");
resolve({ imgURI });
}
};
img.src = svgUrl;
});
});
}
exportToSVG() {
this.svgUrl().then((url) => {
this.triggerDownload(
url,
this.w.config.chart.toolbar.export.svg.filename,
".svg"
);
});
}
exportToPng() {
const scale = this.w.config.chart.toolbar.export.scale;
const width = this.w.config.chart.toolbar.export.width;
const option = scale ? { scale } : width ? { width } : void 0;
this.dataURI(option).then(({ imgURI, blob }) => {
if (blob) {
navigator.msSaveOrOpenBlob(blob, this.w.globals.chartID + ".png");
} else {
this.triggerDownload(
imgURI,
this.w.config.chart.toolbar.export.png.filename,
".png"
);
}
});
}
/** @param {{ series?: any, fileName?: any, columnDelimiter?: string, lineDelimiter?: string }} opts */
exportToCSV({
series,
fileName,
columnDelimiter = ",",
lineDelimiter = "\n"
}) {
const w = this.w;
if (!series) series = w.config.series;
let columns = [];
const rows = [];
let result = "";
const universalBOM = "\uFEFF";
const gSeries = w.seriesData.series.map((s, i) => {
return w.globals.collapsedSeriesIndices.indexOf(i) === -1 ? s : [];
});
const getFormattedCategory = (cat) => {
if (typeof w.config.chart.toolbar.export.csv.categoryFormatter === "function") {
return w.config.chart.toolbar.export.csv.categoryFormatter(cat);
}
if (w.config.xaxis.type === "datetime" && String(cat).length >= 10) {
return new Date(cat).toDateString();
}
return Utils$1.isNumber(cat) ? cat : cat.split(columnDelimiter).join("");
};
const getFormattedValue = (value) => {
return typeof w.config.chart.toolbar.export.csv.valueFormatter === "function" ? w.config.chart.toolbar.export.csv.valueFormatter(value) : value;
};
const seriesMaxDataLength = Math.max(
...series.map((s) => {
return s.data ? s.data.length : 0;
})
);
const dataFormat = new Data(this.w);
const axesUtils = new AxesUtils(this.w, {
theme: this.ctx.theme,
timeScale: this.ctx.timeScale
});
const getCat = (i) => {
let cat = "";
if (!w.globals.axisCharts) {
cat = w.config.labels[i];
} else {
if (w.config.xaxis.type === "category" || w.config.xaxis.convertedCatToNumeric) {
if (w.globals.isBarHorizontal) {
const lbFormatter = w.formatters.yLabelFormatters[0];
const sr = new Series(this.ctx.w);
const activeSeries = sr.getActiveConfigSeriesIndex();
cat = lbFormatter(w.labelData.labels[i], {
seriesIndex: activeSeries,
dataPointIndex: i,
w
});
} else {
cat = axesUtils.getLabel(
w.labelData.labels,
w.labelData.timescaleLabels,
0,
i
).text;
}
}
if (w.config.xaxis.type === "datetime") {
if (w.config.xaxis.categories.length) {
cat = w.config.xaxis.categories[i];
} else if (w.config.labels.length) {
cat = w.config.labels[i];
}
}
}
if (cat === null) return "nullvalue";
if (Array.isArray(cat)) {
cat = cat.join(" ");
}
return Utils$1.isNumber(cat) ? cat : cat.split(columnDelimiter).join("");
};
const getEmptyDataForCsvColumn = () => {
return [...Array(seriesMaxDataLength)].map(() => "");
};
const handleAxisRowsColumns = (s, sI) => {
var _a;
if (columns.length && sI === 0) {
rows.push(columns.join(columnDelimiter));
}
if (s.data) {
s.data = s.data.length && s.data || getEmptyDataForCsvColumn();
for (let i = 0; i < s.data.length; i++) {
columns = [];
let cat = getCat(i);
if (cat === "nullvalue") continue;
if (!cat) {
if (dataFormat.isFormatXY()) {
cat = series[sI].data[i].x;
} else if (dataFormat.isFormat2DArray()) {
cat = series[sI].data[i] ? series[sI].data[i][0] : "";
}
}
if (sI === 0) {
columns.push(getFormattedCategory(cat));
for (let ci = 0; ci < w.seriesData.series.length; ci++) {
const value = dataFormat.isFormatXY() ? (_a = series[ci].data[i]) == null ? void 0 : _a.y : gSeries[ci][i];
columns.push(getFormattedValue(value));
}
}
if (w.config.chart.type === "candlestick" || s.type && s.type === "candlestick") {
columns.pop();
columns.push(w.candleData.seriesCandleO[sI][i]);
columns.push(w.candleData.seriesCandleH[sI][i]);
columns.push(w.candleData.seriesCandleL[sI][i]);
columns.push(w.candleData.seriesCandleC[sI][i]);
}
if (w.config.chart.type === "boxPlot" || s.type && s.type === "boxPlot") {
columns.pop();
columns.push(w.candleData.seriesCandleO[sI][i]);
columns.push(w.candleData.seriesCandleH[sI][i]);
columns.push(w.candleData.seriesCandleM[sI][i]);
columns.push(w.candleData.seriesCandleL[sI][i]);
columns.push(w.candleData.seriesCandleC[sI][i]);
}
if (w.config.chart.type === "rangeBar") {
columns.pop();
columns.push(w.rangeData.seriesRangeStart[sI][i]);
columns.push(w.rangeData.seriesRangeEnd[sI][i]);
}
if (columns.length) {
rows.push(columns.join(columnDelimiter));
}
}
}
};
const handleUnequalXValues = () => {
const categories = /* @__PURE__ */ new Set();
const data = {};
series.forEach((s, sI) => {
s == null ? void 0 : s.data.forEach((dataItem) => {
let cat, value;
if (dataFormat.isFormatXY()) {
cat = dataItem.x;
value = dataItem.y;
} else if (dataFormat.isFormat2DArray()) {
cat = dataItem[0];
value = dataItem[1];
} else {
return;
}
if (!/** @type {Record<string,any>} */
data[cat]) {
data[cat] = Array(
series.length
).fill("");
}
data[cat][sI] = getFormattedValue(value);
categories.add(cat);
});
});
if (columns.length) {
rows.push(columns.join(columnDelimiter));
}
Array.from(categories).sort().forEach((cat) => {
rows.push([
getFormattedCategory(cat),
/** @type {Record<string,any>} */
data[cat].join(columnDelimiter)
]);
});
};
columns.push(w.config.chart.toolbar.export.csv.headerCategory);
if (w.config.chart.type === "boxPlot") {
columns.push("minimum");
columns.push("q1");
columns.push("median");
columns.push("q3");
columns.push("maximum");
} else if (w.config.chart.type === "candlestick") {
columns.push("open");
columns.push("high");
columns.push("low");
columns.push("close");
} else if (w.config.chart.type === "rangeBar") {
columns.push("minimum");
columns.push("maximum");
} else {
series.map((s, sI) => {
const sname = (s.name ? s.name : `series-${sI}`) + "";
if (w.globals.axisCharts) {
columns.push(
sname.split(columnDelimiter).join("") ? sname.split(columnDelimiter).join("") : `series-${sI}`
);
}
});
}
if (!w.globals.axisCharts) {
columns.push(w.config.chart.toolbar.export.csv.headerValue);
rows.push(columns.join(columnDelimiter));
}
if (!w.globals.allSeriesHasEqualX && w.globals.axisCharts && !w.config.xaxis.categories.length && !w.config.labels.length) {
handleUnequalXValues();
} else {
series.map((s, sI) => {
if (w.globals.axisCharts) {
handleAxisRowsColumns(s, sI);
} else {
columns = [];
columns.push(getFormattedCategory(w.labelData.labels[sI]));
columns.push(getFormattedValue(gSeries[sI]));
rows.push(columns.join(columnDelimiter));
}
});
}
result += rows.join(lineDelimiter);
this.triggerDownload(
"data:text/csv; charset=utf-8," + encodeURIComponent(universalBOM + result),
fileName ? fileName : w.config.chart.toolbar.export.csv.filename,
".csv"
);
}
/**
* @param {string} href
* @param {string} filename
* @param {string} ext
*/
triggerDownload(href, filename, ext) {
if (Environment.isSSR()) return;
const downloadLink = document.createElement("a");
downloadLink.href = href;
downloadLink.download = (filename ? filename : this.w.globals.chartID) + ext;
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
}
}
ApexCharts.registerFeatures({ exports: Exports });
let Helpers$3 = class Helpers2 {
/**
* @param {import('./Legend').default} lgCtx
*/
constructor(lgCtx) {
this.w = lgCtx.w;
this.lgCtx = lgCtx;
}
getLegendStyles() {
if (Environment.isSSR()) return null;
const stylesheet = document.createElement("style");
stylesheet.setAttribute("type", "text/css");
const nonce = this.w.config.chart.nonce;
if (nonce) {
stylesheet.setAttribute("nonce", nonce);
}
const rule = document.createTextNode(apexchartsLegendCSS);
stylesheet.appendChild(rule);
return stylesheet;
}
getLegendDimensions() {
const w = this.w;
const currLegendsWrap = w.dom.baseEl.querySelector(".apexcharts-legend");
if (!currLegendsWrap) {
return { clwh: 0, clww: 0 };
}
const { width: currLegendsWrapWidth, height: currLegendsWrapHeight } = currLegendsWrap.getBoundingClientRect();
return {
clwh: currLegendsWrapHeight,
clww: currLegendsWrapWidth
};
}
appendToForeignObject() {
var _a;
const legendStyles = this.getLegendStyles();
if (this.w.config.chart.injectStyleSheet !== false && legendStyles) {
(_a = this.w.dom.elLegendForeign) == null ? void 0 : _a.appendChild(legendStyles);
}
}
/**
* @param {number} seriesCnt
* @param {boolean} isHidden
*/
toggleDataSeries(seriesCnt, isHidden) {
var _a, _b;
const w = this.w;
if (w.globals.axisCharts || w.config.chart.type === "radialBar") {
w.globals.resized = true;
let seriesEl = null;
let realIndex = null;
w.globals.risingSeries = [];
if (w.globals.axisCharts) {
seriesEl = w.dom.baseEl.querySelector(
`.apexcharts-series[data\\:realIndex='${seriesCnt}']`
);
if (!seriesEl) return;
realIndex = parseInt((_a = seriesEl.getAttribute("data:realIndex")) != null ? _a : "", 10);
} else {
seriesEl = w.dom.baseEl.querySelector(
`.apexcharts-series[rel='${seriesCnt + 1}']`
);
if (!seriesEl) return;
realIndex = parseInt((_b = seriesEl.getAttribute("rel")) != null ? _b : "", 10) - 1;
}
if (isHidden) {
const seriesToMakeVisible = [
{
cs: w.globals.collapsedSeries,
csi: w.globals.collapsedSeriesIndices
},
{
cs: w.globals.ancillaryCollapsedSeries,
csi: w.globals.ancillaryCollapsedSeriesIndices
}
];
seriesToMakeVisible.forEach((r) => {
const cs = (
/** @type {any} */
r.cs
);
const csi = (
/** @type {any} */
r.csi
);
this.riseCollapsedSeries(
cs,
csi,
/** @type {number} */
realIndex
);
});
} else {
this.hideSeries({ seriesEl, realIndex });
}
if (w.config.chart.accessibility.enabled) {
const legendItem = w.dom.baseEl.querySelector(
`.apexcharts-legend-series[rel="${seriesCnt + 1}"]`
);
if (legendItem) {
const isCollapsed = w.globals.collapsedSeriesIndices.includes(realIndex) || w.globals.ancillaryCollapsedSeriesIndices.includes(realIndex);
legendItem.setAttribute(
"aria-pressed",
isCollapsed ? "true" : "false"
);
const legendTextEl = legendItem.querySelector(
".apexcharts-legend-text"
);
const seriesName = legendTextEl ? legendTextEl.textContent : w.seriesData.seriesNames[seriesCnt];
const statusText = isCollapsed ? "hidden" : "visible";
legendItem.setAttribute(
"aria-label",
`${seriesName}, ${statusText}. Press Enter or Space to toggle.`
);
}
}
} else {
const seriesEl = w.dom.Paper.findOne(
` .apexcharts-series[rel='${seriesCnt + 1}'] path`
);
const type = w.config.chart.type;
if (type === "pie" || type === "polarArea" || type === "donut") {
const dataLabels = w.config.plotOptions.pie.donut.labels;
const graphics = new Graphics(this.w);
graphics.pathMouseDown(seriesEl, null);
this.lgCtx.printDataLabelsInner(seriesEl.node, dataLabels);
}
if (w.config.chart.accessibility.enabled) {
const legendItem = w.dom.baseEl.querySelector(
`.apexcharts-legend-series[rel="${seriesCnt + 1}"]`
);
if (legendItem) {
const isCollapsed = w.globals.collapsedSeriesIndices.includes(seriesCnt);
legendItem.setAttribute(
"aria-pressed",
isCollapsed ? "true" : "false"
);
const legendTextEl = legendItem.querySelector(
".apexcharts-legend-text"
);
const seriesName = legendTextEl ? legendTextEl.textContent : w.seriesData.seriesNames[seriesCnt];
const statusText = isCollapsed ? "hidden" : "visible";
legendItem.setAttribute(
"aria-label",
`${seriesName}, ${statusText}. Press Enter or Space to toggle.`
);
}
}
}
}
/** @param {{realIndex: any}} opts */
getSeriesAfterCollapsing({ realIndex }) {
var _a;
const w = this.w;
const gl = w.globals;
const series = Utils$1.clone(w.config.series);
if (gl.axisCharts) {
const yaxis = w.config.yaxis[gl.seriesYAxisReverseMap[realIndex]];
const collapseData = {
index: realIndex,
data: series[realIndex].data.slice(),
type: series[realIndex].type || w.config.chart.type
};
if (yaxis && yaxis.show && yaxis.showAlways) {
if (gl.ancillaryCollapsedSeriesIndices.indexOf(realIndex) < 0) {
gl.ancillaryCollapsedSeries.push(collapseData);
gl.ancillaryCollapsedSeriesIndices.push(realIndex);
}
} else {
if (gl.collapsedSeriesIndices.indexOf(realIndex) < 0) {
gl.collapsedSeries.push(collapseData);
gl.collapsedSeriesIndices.push(realIndex);
const removeIndexOfRising = gl.risingSeries.indexOf(realIndex);
gl.risingSeries.splice(removeIndexOfRising, 1);
}
}
} else {
gl.collapsedSeries.push({
index: realIndex,
data: series[realIndex],
type: (
/** @type {any} */
(_a = w.config.series[realIndex].type) != null ? _a : "line"
)
});
gl.collapsedSeriesIndices.push(realIndex);
}
gl.allSeriesCollapsed = gl.collapsedSeries.length + gl.ancillaryCollapsedSeries.length === w.config.series.length;
return this._getSeriesBasedOnCollapsedState(series);
}
/** @param {{seriesEl: any, realIndex: any}} opts */
hideSeries({ seriesEl, realIndex }) {
const w = this.w;
const series = this.getSeriesAfterCollapsing({
realIndex
});
const seriesChildren = seriesEl.childNodes;
for (let sc = 0; sc < seriesChildren.length; sc++) {
if (seriesChildren[sc].classList.contains("apexcharts-series-markers-wrap")) {
if (seriesChildren[sc].classList.contains("apexcharts-hide")) {
seriesChildren[sc].classList.remove("apexcharts-hide");
} else {
seriesChildren[sc].classList.add("apexcharts-hide");
}
}
}
this.lgCtx.updateSeries(
series,
w.config.chart.animations.dynamicAnimation.enabled
);
}
/**
* @param {any[]} collapsedSeries
* @param {number[]} seriesIndices
* @param {number} realIndex
*/
riseCollapsedSeries(collapsedSeries, seriesIndices, realIndex) {
const w = this.w;
let series = Utils$1.clone(w.config.series);
if (collapsedSeries.length > 0) {
for (let c = 0; c < collapsedSeries.length; c++) {
if (collapsedSeries[c].index === realIndex) {
if (w.globals.axisCharts) {
series[realIndex].data = collapsedSeries[c].data.slice();
} else {
series[realIndex] = collapsedSeries[c].data;
}
if (typeof series[realIndex] !== "number") {
series[realIndex].hidden = false;
}
collapsedSeries.splice(c, 1);
seriesIndices.splice(c, 1);
w.globals.risingSeries.push(realIndex);
c--;
}
}
series = this._getSeriesBasedOnCollapsedState(series);
this.lgCtx.updateSeries(
series,
w.config.chart.animations.dynamicAnimation.enabled
);
}
}
/**
* @param {any[]} series
*/
_getSeriesBasedOnCollapsedState(series) {
const w = this.w;
let collapsed = 0;
if (w.globals.axisCharts) {
series.forEach((s, sI) => {
if (!(w.globals.collapsedSeriesIndices.indexOf(sI) < 0 && w.globals.ancillaryCollapsedSeriesIndices.indexOf(sI) < 0)) {
series[sI].data = [];
collapsed++;
}
});
} else {
series.forEach((s, sI) => {
if (!(w.globals.collapsedSeriesIndices.indexOf(sI) < 0)) {
series[sI] = 0;
collapsed++;
}
});
}
w.globals.allSeriesCollapsed = collapsed === series.length;
return series;
}
};
class Legend {
/**
* @param {import('../../types/internal').ChartStateW} w
* @param {import('../../types/internal').ChartContext} ctx
*/
constructor(w, ctx) {
this.w = w;
this.ctx = ctx;
this.printDataLabelsInner = (...a) => {
var _a;
return (_a = ctx.pie) == null ? void 0 : _a.printDataLabelsInner(...a);
};
this.updateSeries = (...a) => ctx.updateHelpers._updateSeries(...a);
this.onLegendClick = this.onLegendClick.bind(this);
this.onLegendHovered = this.onLegendHovered.bind(this);
this.isBarsDistributed = this.w.config.chart.type === "bar" && this.w.config.plotOptions.bar.distributed && this.w.config.series.length === 1;
this.legendHelpers = new Helpers$3(this);
}
init() {
const w = this.w;
const gl = w.globals;
const cnf = w.config;
const showLegendAlways = cnf.legend.showForSingleSeries && this.w.seriesData.series.length === 1 || this.isBarsDistributed || this.w.seriesData.series.length > 1;
this.legendHelpers.appendToForeignObject();
if ((showLegendAlways || !gl.axisCharts) && cnf.legend.show) {
const elLegendWrap = (
/** @type {HTMLElement} */
w.dom.elLegendWrap
);
while (elLegendWrap.firstChild) {
elLegendWrap.removeChild(elLegendWrap.firstChild);
}
this.drawLegends();
if (cnf.legend.position === "bottom" || cnf.legend.position === "top") {
this.legendAlignHorizontal();
} else if (cnf.legend.position === "right" || cnf.legend.position === "left") {
this.legendAlignVertical();
}
}
}
createLegendMarker({ i, fillcolor }) {
const w = this.w;
const elMarker = BrowserAPIs.createElement("span");
elMarker.classList.add("apexcharts-legend-marker");
const mShape = w.config.legend.markers.shape || w.config.markers.shape;
let shape = mShape;
if (Array.isArray(mShape)) {
shape = mShape[i];
}
const mSize = Array.isArray(w.config.legend.markers.size) ? parseFloat(w.config.legend.markers.size[i]) : parseFloat(w.config.legend.markers.size);
const mOffsetX = Array.isArray(w.config.legend.markers.offsetX) ? parseFloat(w.config.legend.markers.offsetX[i]) : parseFloat(w.config.legend.markers.offsetX);
const mOffsetY = Array.isArray(w.config.legend.markers.offsetY) ? parseFloat(w.config.legend.markers.offsetY[i]) : parseFloat(w.config.legend.markers.offsetY);
const mBorderWidth = Array.isArray(w.config.legend.markers.strokeWidth) ? parseFloat(w.config.legend.markers.strokeWidth[i]) : parseFloat(w.config.legend.markers.strokeWidth);
const mStyle = elMarker.style;
mStyle.height = (mSize + mBorderWidth) * 2 + "px";
mStyle.width = (mSize + mBorderWidth) * 2 + "px";
mStyle.left = mOffsetX + "px";
mStyle.top = mOffsetY + "px";
if (w.config.legend.markers.customHTML) {
mStyle.background = "transparent";
mStyle.color = fillcolor[i];
if (Array.isArray(w.config.legend.markers.customHTML)) {
if (w.config.legend.markers.customHTML[i]) {
elMarker.innerHTML = w.config.legend.markers.customHTML[i]();
}
} else {
elMarker.innerHTML = w.config.legend.markers.customHTML();
}
} else {
const markers = new Markers(this.ctx.w, this.ctx);
const markerConfig = markers.getMarkerConfig({
cssClass: `apexcharts-legend-marker apexcharts-marker apexcharts-marker-${shape}`,
seriesIndex: i,
strokeWidth: mBorderWidth,
size: mSize
});
const SVGLib = Environment.isBrowser() ? (
/** @type {any} */
window.SVG
) : (
/** @type {any} */
global.SVG
);
const SVGMarker = SVGLib().addTo(elMarker).size("100%", "100%");
const marker = new Graphics(this.w).drawMarker(0, 0, __spreadProps(__spreadValues({}, markerConfig), {
pointFillColor: Array.isArray(fillcolor) ? fillcolor[i] : markerConfig.pointFillColor,
shape
}));
const shapesEls = w.dom.Paper.find(
".apexcharts-legend-marker.apexcharts-marker"
);
shapesEls.forEach((shapeEl) => {
if (shapeEl.node.classList.contains("apexcharts-marker-triangle")) {
shapeEl.node.style.transform = "translate(50%, 45%)";
} else {
shapeEl.node.style.transform = "translate(50%, 50%)";
}
});
SVGMarker.add(marker);
}
return elMarker;
}
drawLegends() {
var _a;
const me = this;
const w = this.w;
const elLegendWrap = (
/** @type {HTMLElement} */
w.dom.elLegendWrap
);
const fontFamily = w.config.legend.fontFamily;
let legendNames = w.seriesData.seriesNames;
let fillcolor = w.config.legend.markers.fillColors ? w.config.legend.markers.fillColors.slice() : w.globals.colors.slice();
if (w.config.chart.type === "heatmap") {
const ranges = w.config.plotOptions.heatmap.colorScale.ranges;
legendNames = ranges.map((colorScale) => {
return colorScale.name ? colorScale.name : colorScale.from + " - " + colorScale.to;
});
fillcolor = ranges.map((color) => color.color);
} else if (this.isBarsDistributed) {
legendNames = w.labelData.labels.slice();
}
if (w.config.legend.customLegendItems.length) {
legendNames = w.config.legend.customLegendItems;
}
const legendFormatter = w.formatters.legendFormatter;
const isLegendInversed = w.config.legend.inverseOrder;
const legendGroups = [];
if (w.labelData.seriesGroups.length > 1 && w.config.legend.clusterGroupedSeries) {
w.labelData.seriesGroups.forEach((_, gi) => {
legendGroups[gi] = BrowserAPIs.createElement("div");
legendGroups[gi].classList.add(
"apexcharts-legend-group",
`apexcharts-legend-group-${gi}`
);
if (w.config.legend.clusterGroupedSeriesOrientation === "horizontal") {
elLegendWrap.classList.add("apexcharts-legend-group-horizontal");
} else {
legendGroups[gi].classList.add("apexcharts-legend-group-vertical");
}
});
}
for (let i = isLegendInversed ? legendNames.length - 1 : 0; isLegendInversed ? i >= 0 : i <= legendNames.length - 1; isLegendInversed ? i-- : i++) {
const text = legendFormatter(legendNames[i], { seriesIndex: i, w });
let collapsedSeries = false;
let ancillaryCollapsedSeries = false;
if (w.globals.collapsedSeries.length > 0) {
for (let c = 0; c < w.globals.collapsedSeries.length; c++) {
if (w.globals.collapsedSeries[c].index === i) {
collapsedSeries = true;
}
}
}
if (w.globals.ancillaryCollapsedSeriesIndices.length > 0) {
for (let c = 0; c < w.globals.ancillaryCollapsedSeriesIndices.length; c++) {
if (w.globals.ancillaryCollapsedSeriesIndices[c] === i) {
ancillaryCollapsedSeries = true;
}
}
}
const elMarker = this.createLegendMarker({ i, fillcolor });
Graphics.setAttrs(elMarker, {
rel: i + 1,
"data:collapsed": collapsedSeries || ancillaryCollapsedSeries
});
if (collapsedSeries || ancillaryCollapsedSeries) {
elMarker.classList.add("apexcharts-inactive-legend");
}
const elLegend = BrowserAPIs.createElement("div");
if (w.config.chart.accessibility.enabled && w.config.chart.accessibility.keyboard.enabled) {
elLegend.setAttribute("role", "button");
elLegend.setAttribute("tabindex", "0");
const seriesName = Array.isArray(text) ? text.join(" ") : text;
const isCollapsed = collapsedSeries || ancillaryCollapsedSeries;
const statusText = isCollapsed ? "hidden" : "visible";
elLegend.setAttribute(
"aria-label",
`${seriesName}, ${statusText}. Press Enter or Space to toggle.`
);
elLegend.setAttribute("aria-pressed", isCollapsed ? "true" : "false");
}
const elLegendText = BrowserAPIs.createElement("span");
elLegendText.classList.add("apexcharts-legend-text");
elLegendText.innerHTML = Array.isArray(text) ? text.join(" ") : text;
let textColor = w.config.legend.labels.useSeriesColors ? w.globals.colors[i] : Array.isArray(w.config.legend.labels.colors) ? (_a = w.config.legend.labels.colors) == null ? void 0 : _a[i] : w.config.legend.labels.colors;
if (!textColor) {
textColor = w.config.chart.foreColor;
}
elLegendText.style.color = textColor;
elLegendText.style.fontSize = w.config.legend.fontSize;
elLegendText.style.fontWeight = w.config.legend.fontWeight;
elLegendText.style.fontFamily = fontFamily || w.config.chart.fontFamily;
Graphics.setAttrs(elLegendText, {
rel: i + 1,
i,
"data:default-text": encodeURIComponent(text),
"data:collapsed": collapsedSeries || ancillaryCollapsedSeries
});
elLegend.appendChild(elMarker);
elLegend.appendChild(elLegendText);
const coreUtils = new CoreUtils(this.w);
if (!w.config.legend.showForZeroSeries) {
const total = coreUtils.getSeriesTotalByIndex(i);
if (total === 0 && coreUtils.seriesHaveSameValues(i) && !coreUtils.isSeriesNull(i) && w.globals.collapsedSeriesIndices.indexOf(i) === -1 && w.globals.ancillaryCollapsedSeriesIndices.indexOf(i) === -1) {
elLegend.classList.add("apexcharts-hidden-zero-series");
}
}
if (!w.config.legend.showForNullSeries) {
if (coreUtils.isSeriesNull(i) && w.globals.collapsedSeriesIndices.indexOf(i) === -1 && w.globals.ancillaryCollapsedSeriesIndices.indexOf(i) === -1) {
elLegend.classList.add("apexcharts-hidden-null-series");
}
}
if (legendGroups.length) {
w.labelData.seriesGroups.forEach((group, gi) => {
var _a2, _b;
if (group.includes(
/** @type {Record<string,any>} */
(_b = (_a2 = w.config.series[i]) == null ? void 0 : _a2.name) != null ? _b : ""
)) {
elLegendWrap.appendChild(legendGroups[gi]);
legendGroups[gi].appendChild(elLegend);
}
});
} else {
elLegendWrap.appendChild(elLegend);
}
elLegendWrap.classList.add(
`apexcharts-align-${w.config.legend.horizontalAlign}`
);
elLegendWrap.classList.add(
"apx-legend-position-" + w.config.legend.position
);
elLegend.classList.add("apexcharts-legend-series");
elLegend.style.margin = `${w.config.legend.itemMargin.vertical}px ${w.config.legend.itemMargin.horizontal}px`;
elLegendWrap.style.width = w.config.legend.width ? w.config.legend.width + "px" : "";
elLegendWrap.style.height = w.config.legend.height ? w.config.legend.height + "px" : "";
Graphics.setAttrs(elLegend, {
rel: i + 1,
seriesName: Utils$1.escapeString(legendNames[i]),
"data:collapsed": collapsedSeries || ancillaryCollapsedSeries
});
if (collapsedSeries || ancillaryCollapsedSeries) {
elLegend.classList.add("apexcharts-inactive-legend");
}
if (!w.config.legend.onItemClick.toggleDataSeries) {
elLegend.classList.add("apexcharts-no-click");
}
}
w.dom.elWrap.addEventListener("click", me.onLegendClick, true);
if (w.config.legend.onItemHover.highlightDataSeries && w.config.legend.customLegendItems.length === 0) {
w.dom.elWrap.addEventListener("mousemove", me.onLegendHovered, true);
w.dom.elWrap.addEventListener("mouseout", me.onLegendHovered, true);
}
if (w.config.chart.accessibility.enabled && w.config.chart.accessibility.keyboard.enabled) {
w.dom.elWrap.addEventListener(
"keydown",
me.onLegendKeyDown.bind(me),
true
);
}
}
/**
* @param {number} offsetX
* @param {number} offsetY
*/
setLegendWrapXY(offsetX, offsetY) {
const w = this.w;
const elLegendWrap = (
/** @type {HTMLElement} */
w.dom.elLegendWrap
);
const legendHeight = elLegendWrap.clientHeight;
let x = 0;
let y = 0;
if (w.config.legend.position === "bottom") {
y = w.globals.svgHeight - Math.min(legendHeight, w.globals.svgHeight / 2) - 5;
} else if (w.config.legend.position === "top") {
const dim = new Dimensions(this.w, this.ctx);
const titleH = dim.dimHelpers.getTitleSubtitleCoords("title").height;
const subtitleH = dim.dimHelpers.getTitleSubtitleCoords("subtitle").height;
y = (titleH > 0 ? titleH - 10 : 0) + (subtitleH > 0 ? subtitleH - 10 : 0);
}
elLegendWrap.style.position = "absolute";
x = x + offsetX + w.config.legend.offsetX;
y = y + offsetY + w.config.legend.offsetY;
elLegendWrap.style.left = x + "px";
elLegendWrap.style.top = y + "px";
if (w.config.legend.position === "right") {
elLegendWrap.style.left = "auto";
elLegendWrap.style.right = 25 + w.config.legend.offsetX + "px";
}
const fixedHeigthWidth = (
/** @type {const} */
["width", "height"]
);
fixedHeigthWidth.forEach((hw) => {
if (elLegendWrap && elLegendWrap.style[hw]) {
elLegendWrap.style[hw] = parseInt(String(w.config.legend[hw]), 10) + "px";
}
});
}
legendAlignHorizontal() {
const w = this.w;
const elLegendWrap = (
/** @type {HTMLElement} */
w.dom.elLegendWrap
);
elLegendWrap.style.right = "0";
const dimensions = new Dimensions(this.w, this.ctx);
const titleRect = dimensions.dimHelpers.getTitleSubtitleCoords("title");
const subtitleRect = dimensions.dimHelpers.getTitleSubtitleCoords("subtitle");
const offsetX = 20;
let offsetY = 0;
if (w.config.legend.position === "top") {
offsetY = titleRect.height + subtitleRect.height + w.config.title.margin + w.config.subtitle.margin - 10;
}
this.setLegendWrapXY(offsetX, offsetY);
}
legendAlignVertical() {
const w = this.w;
const lRect = this.legendHelpers.getLegendDimensions();
const offsetY = 20;
let offsetX = 0;
if (w.config.legend.position === "left") {
offsetX = 20;
}
if (w.config.legend.position === "right") {
offsetX = w.globals.svgWidth - lRect.clww - 10;
}
this.setLegendWrapXY(offsetX, offsetY);
}
/**
* @param {MouseEvent} e
*/
onLegendHovered(e) {
var _a;
const w = this.w;
const target = (
/** @type {Element} */
e.target
);
const hoverOverLegend = target.classList.contains("apexcharts-legend-series") || target.classList.contains("apexcharts-legend-text") || target.classList.contains("apexcharts-legend-marker");
if (w.config.chart.type !== "heatmap" && !this.isBarsDistributed) {
if (!target.classList.contains("apexcharts-inactive-legend") && hoverOverLegend) {
const series = new Series(this.ctx.w);
series.toggleSeriesOnHover(e, target);
}
} else {
if (hoverOverLegend) {
const seriesCnt = parseInt((_a = target.getAttribute("rel")) != null ? _a : "0", 10) - 1;
this.ctx.events.fireEvent("legendHover", [this.ctx, seriesCnt, this.w]);
const series = new Series(this.ctx.w);
series.highlightRangeInSeries(e, target);
}
}
}
/**
* @param {KeyboardEvent} e
*/
onLegendKeyDown(e) {
const me = this;
const w = this.w;
const target = (
/** @type {Element} */
e.target
);
const isLegendItem = target.classList.contains("apexcharts-legend-series") || target.classList.contains("apexcharts-legend-text") || target.classList.contains("apexcharts-legend-marker");
if (!isLegendItem) return;
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
const rel = target.getAttribute("rel");
me.onLegendClick(e);
if (rel !== null && w.config.legend.onItemClick.toggleDataSeries) {
requestAnimationFrame(() => {
const restored = w.dom.baseEl.querySelector(
`.apexcharts-legend-series[rel="${rel}"]`
);
if (restored) restored.focus();
});
}
}
}
/**
* @param {Event} e
*/
onLegendClick(e) {
var _a;
const w = this.w;
const target = (
/** @type {Element} */
e.target
);
if (w.config.legend.customLegendItems.length) return;
if (target.classList.contains("apexcharts-legend-series") || target.classList.contains("apexcharts-legend-text") || target.classList.contains("apexcharts-legend-marker")) {
const seriesCnt = parseInt((_a = target.getAttribute("rel")) != null ? _a : "0", 10) - 1;
const isHidden = target.getAttribute("data:collapsed") === "true";
const legendClick = this.w.config.chart.events.legendClick;
if (typeof legendClick === "function") {
legendClick(this.ctx, seriesCnt, this.w);
}
this.ctx.events.fireEvent("legendClick", [this.ctx, seriesCnt, this.w]);
const markerClick = this.w.config.legend.markers.onClick;
if (typeof markerClick === "function" && target.classList.contains("apexcharts-legend-marker")) {
markerClick(this.ctx, seriesCnt, this.w);
this.ctx.events.fireEvent("legendMarkerClick", [
this.ctx,
seriesCnt,
this.w
]);
}
const clickAllowed = w.config.chart.type !== "treemap" && w.config.chart.type !== "heatmap" && !this.isBarsDistributed;
if (clickAllowed && w.config.legend.onItemClick.toggleDataSeries) {
this.legendHelpers.toggleDataSeries(seriesCnt, isHidden);
}
}
}
}
ApexCharts.registerFeatures({ legend: Legend });
const icoPan = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#000000" height="24" viewBox="0 0 24 24" width="24">\n <defs>\n <path d="M0 0h24v24H0z" id="a"/>\n </defs>\n <clipPath id="b">\n <use overflow="visible" xlink:href="#a"/>\n </clipPath>\n <path clip-path="url(#b)" d="M23 5.5V20c0 2.2-1.8 4-4 4h-7.3c-1.08 0-2.1-.43-2.85-1.19L1 14.83s1.26-1.23 1.3-1.25c.22-.19.49-.29.79-.29.22 0 .42.06.6.16.04.01 4.31 2.46 4.31 2.46V4c0-.83.67-1.5 1.5-1.5S11 3.17 11 4v7h1V1.5c0-.83.67-1.5 1.5-1.5S15 .67 15 1.5V11h1V2.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5V11h1V5.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5z"/>\n</svg>';
const icoZoom = '<svg xmlns="http://www.w3.org/2000/svg" fill="#000000" height="24" viewBox="0 0 24 24" width="24">\n <path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>\n <path d="M0 0h24v24H0V0z" fill="none"/>\n <path d="M12 10h-2v2H9v-2H7V9h2V7h1v2h2v1z"/>\n</svg>';
const icoReset = '<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">\n <path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>\n <path d="M0 0h24v24H0z" fill="none"/>\n</svg>';
const icoZoomIn = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">\n <path d="M0 0h24v24H0z" fill="none"/>\n <path d="M13 7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/>\n</svg>\n';
const icoZoomOut = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">\n <path d="M0 0h24v24H0z" fill="none"/>\n <path d="M7 11v2h10v-2H7zm5-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/>\n</svg>\n';
const icoSelect = '<svg fill="#6E8192" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">\n <path d="M0 0h24v24H0z" fill="none"/>\n <path d="M3 5h2V3c-1.1 0-2 .9-2 2zm0 8h2v-2H3v2zm4 8h2v-2H7v2zM3 9h2V7H3v2zm10-6h-2v2h2V3zm6 0v2h2c0-1.1-.9-2-2-2zM5 21v-2H3c0 1.1.9 2 2 2zm-2-4h2v-2H3v2zM9 3H7v2h2V3zm2 18h2v-2h-2v2zm8-8h2v-2h-2v2zm0 8c1.1 0 2-.9 2-2h-2v2zm0-12h2V7h-2v2zm0 8h2v-2h-2v2zm-4 4h2v-2h-2v2zm0-16h2V3h-2v2z"/>\n</svg>';
const icoMenu = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/></svg>';
class Toolbar {
/**
* @param {import('../types/internal').ChartStateW} w
* @param {import('../types/internal').ChartContext} ctx
*/
constructor(w, ctx) {
this.w = w;
this.ctx = ctx;
this.ev = this.w.config.chart.events;
this.selectedClass = "apexcharts-selected";
this.localeValues = this.w.globals.locale.toolbar;
this.minX = w.globals.minX;
this.maxX = w.globals.maxX;
this.elZoom = null;
this.elZoomIn = null;
this.elZoomOut = null;
this.elPan = null;
this.elSelection = null;
this.elZoomReset = null;
this.elMenuIcon = null;
this.elMenu = null;
this.elMenuItems = [];
this.t = null;
}
createToolbar() {
const w = this.w;
const createDiv = () => {
return BrowserAPIs.createElementNS("http://www.w3.org/1999/xhtml", "div");
};
const createBtn = () => {
const btn = (
/** @type {HTMLButtonElement} */
BrowserAPIs.createElementNS("http://www.w3.org/1999/xhtml", "button")
);
btn.setAttribute("type", "button");
return btn;
};
const elToolbarWrap = createDiv();
elToolbarWrap.setAttribute("class", "apexcharts-toolbar");
elToolbarWrap.style.top = w.config.chart.toolbar.offsetY + "px";
elToolbarWrap.style.right = -w.config.chart.toolbar.offsetX + 3 + "px";
w.dom.elWrap.appendChild(elToolbarWrap);
this.elZoom = createBtn();
this.elZoomIn = createBtn();
this.elZoomOut = createBtn();
this.elPan = createBtn();
this.elSelection = createBtn();
this.elZoomReset = createBtn();
this.elMenuIcon = createBtn();
this.elMenu = createDiv();
this.elCustomIcons = [];
this.t = w.config.chart.toolbar.tools;
if (Array.isArray(this.t.customIcons)) {
for (let i = 0; i < this.t.customIcons.length; i++) {
this.elCustomIcons.push(createBtn());
}
}
const toolbarControls = [];
const appendZoomControl = (type, el, ico) => {
const tool = type.toLowerCase();
if (this.t[tool] && w.config.chart.zoom.enabled) {
toolbarControls.push({
el,
icon: typeof this.t[tool] === "string" ? this.t[tool] : ico,
title: (
/** @type {any} */
this.localeValues[type]
),
class: `apexcharts-${tool}-icon`
});
}
};
appendZoomControl("zoomIn", this.elZoomIn, icoZoomIn);
appendZoomControl("zoomOut", this.elZoomOut, icoZoomOut);
const zoomSelectionCtrls = (z) => {
if (this.t[z] && w.config.chart[z].enabled) {
toolbarControls.push({
el: z === "zoom" ? this.elZoom : this.elSelection,
icon: typeof this.t[z] === "string" ? this.t[z] : z === "zoom" ? icoZoom : icoSelect,
title: (
/** @type {any} */
this.localeValues[z === "zoom" ? "selectionZoom" : "selection"]
),
class: `apexcharts-${z}-icon`
});
}
};
zoomSelectionCtrls("zoom");
zoomSelectionCtrls("selection");
if (this.t.pan && w.config.chart.zoom.enabled) {
toolbarControls.push({
el: this.elPan,
icon: typeof this.t.pan === "string" ? this.t.pan : icoPan,
title: this.localeValues.pan,
class: "apexcharts-pan-icon"
});
}
appendZoomControl("reset", this.elZoomReset, icoReset);
if (this.t.download) {
toolbarControls.push({
el: this.elMenuIcon,
icon: typeof this.t.download === "string" ? this.t.download : icoMenu,
title: this.localeValues.menu,
class: "apexcharts-menu-icon"
});
}
for (let i = 0; i < this.elCustomIcons.length; i++) {
toolbarControls.push({
el: this.elCustomIcons[i],
icon: this.t.customIcons[i].icon,
title: this.t.customIcons[i].title,
index: this.t.customIcons[i].index,
class: "apexcharts-toolbar-custom-icon " + this.t.customIcons[i].class
});
}
toolbarControls.forEach((t, index) => {
if (t.index) {
Utils$1.moveIndexInArray(toolbarControls, index, t.index);
}
});
for (let i = 0; i < toolbarControls.length; i++) {
Graphics.setAttrs(toolbarControls[i].el, {
class: toolbarControls[i].class,
title: toolbarControls[i].title,
"aria-label": toolbarControls[i].title
});
toolbarControls[i].el.innerHTML = toolbarControls[i].icon;
elToolbarWrap.appendChild(toolbarControls[i].el);
}
if (this.elZoom.parentNode) {
this.elZoom.setAttribute("aria-pressed", String(!!w.interact.zoomEnabled));
}
if (this.elSelection.parentNode) {
this.elSelection.setAttribute(
"aria-pressed",
String(!!w.interact.selectionEnabled)
);
}
if (this.elPan.parentNode) {
this.elPan.setAttribute("aria-pressed", String(!!w.interact.panEnabled));
}
if (this.elMenuIcon.parentNode) {
this.elMenuIcon.setAttribute("aria-haspopup", "true");
this.elMenuIcon.setAttribute("aria-expanded", "false");
}
this._createHamburgerMenu(elToolbarWrap);
if (w.interact.zoomEnabled) {
this.elZoom.classList.add(this.selectedClass);
} else if (w.interact.panEnabled) {
this.elPan.classList.add(this.selectedClass);
} else if (w.interact.selectionEnabled) {
this.elSelection.classList.add(this.selectedClass);
}
this.addToolbarEventListeners();
}
/**
* @param {Element} parent
*/
_createHamburgerMenu(parent) {
this.elMenuItems = [];
parent.appendChild(
/** @type {Node} */
this.elMenu
);
Graphics.setAttrs(this.elMenu, {
class: "apexcharts-menu",
role: "menu"
});
const menuItems = [
{
name: "exportSVG",
title: this.localeValues.exportToSVG
},
{
name: "exportPNG",
title: this.localeValues.exportToPNG
},
{
name: "exportCSV",
title: this.localeValues.exportToCSV
}
];
for (let i = 0; i < menuItems.length; i++) {
this.elMenuItems.push(
BrowserAPIs.createElementNS("http://www.w3.org/1999/xhtml", "div")
);
this.elMenuItems[i].innerHTML = menuItems[i].title;
Graphics.setAttrs(this.elMenuItems[i], {
class: `apexcharts-menu-item ${menuItems[i].name}`,
title: menuItems[i].title,
role: "menuitem",
tabindex: "-1"
});
this.elMenu.appendChild(this.elMenuItems[i]);
}
}
addToolbarEventListeners() {
var _a, _b, _c, _d, _e, _f, _g, _h;
(_a = this.elZoomReset) == null ? void 0 : _a.addEventListener("click", this.handleZoomReset.bind(this));
(_b = this.elSelection) == null ? void 0 : _b.addEventListener(
"click",
this.toggleZoomSelection.bind(this, "selection")
);
(_c = this.elZoom) == null ? void 0 : _c.addEventListener(
"click",
this.toggleZoomSelection.bind(this, "zoom")
);
(_d = this.elZoomIn) == null ? void 0 : _d.addEventListener("click", this.handleZoomIn.bind(this));
(_e = this.elZoomOut) == null ? void 0 : _e.addEventListener("click", this.handleZoomOut.bind(this));
(_f = this.elPan) == null ? void 0 : _f.addEventListener("click", this.togglePanning.bind(this));
(_g = this.elMenuIcon) == null ? void 0 : _g.addEventListener("click", this.toggleMenu.bind(this));
this.elMenuItems.forEach((m) => {
if (m.classList.contains("exportSVG")) {
m.addEventListener("click", this.handleDownload.bind(this, "svg"));
} else if (m.classList.contains("exportPNG")) {
m.addEventListener("click", this.handleDownload.bind(this, "png"));
} else if (m.classList.contains("exportCSV")) {
m.addEventListener("click", this.handleDownload.bind(this, "csv"));
}
});
for (let i = 0; i < this.t.customIcons.length; i++) {
this.elCustomIcons[i].addEventListener(
"click",
this.t.customIcons[i].click.bind(this, this.ctx, this.ctx.w)
);
}
const toolbarButtons = [
this.elZoomReset,
this.elSelection,
this.elZoom,
this.elZoomIn,
this.elZoomOut,
this.elPan,
this.elMenuIcon,
...this.elCustomIcons
];
toolbarButtons.forEach((btn) => {
btn.addEventListener("keydown", (e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
const btnClass = btn.className;
btn.click();
requestAnimationFrame(() => {
const baseEl = this.w.dom.baseEl;
if (!baseEl) return;
const apexClass = btnClass.split(" ").find((c) => c.startsWith("apexcharts-"));
if (!apexClass) return;
const restored = baseEl.querySelector(`.${apexClass}`);
if (restored) restored.focus();
});
}
});
});
(_h = this.elMenuIcon) == null ? void 0 : _h.addEventListener(
"keydown",
(e) => {
var _a2;
if (e.key === "ArrowDown" || e.key === "ArrowUp") {
e.preventDefault();
if (!((_a2 = this.elMenu) == null ? void 0 : _a2.classList.contains("apexcharts-menu-open"))) {
this.toggleMenu();
}
window.setTimeout(() => {
const idx = e.key === "ArrowDown" ? 0 : this.elMenuItems.length - 1;
if (this.elMenuItems[idx])
this.elMenuItems[idx].focus();
}, 20);
}
}
);
this.elMenuItems.forEach((m, idx) => {
m.addEventListener("keydown", (e) => {
var _a2;
if (e.key === "ArrowDown") {
e.preventDefault();
const next = this.elMenuItems[idx + 1] || this.elMenuItems[0];
next.focus();
} else if (e.key === "ArrowUp") {
e.preventDefault();
const prev = this.elMenuItems[idx - 1] || this.elMenuItems[this.elMenuItems.length - 1];
prev.focus();
} else if (e.key === "Escape" || e.key === "Tab") {
this._closeMenu();
(_a2 = this.elMenuIcon) == null ? void 0 : _a2.focus();
if (e.key === "Tab") ;
else {
e.preventDefault();
}
} else if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
m.click();
}
});
});
}
/**
* @param {string} type
*/
toggleZoomSelection(type) {
const charts = this.ctx.getSyncedCharts();
charts.forEach((ch) => {
ch.ctx.toolbar.toggleOtherControls();
const el = type === "selection" ? ch.ctx.toolbar.elSelection : ch.ctx.toolbar.elZoom;
const enabledType = type === "selection" ? "selectionEnabled" : "zoomEnabled";
ch.w.globals[enabledType] = !ch.w.globals[enabledType];
if (!el.classList.contains(ch.ctx.toolbar.selectedClass)) {
el.classList.add(ch.ctx.toolbar.selectedClass);
} else {
el.classList.remove(ch.ctx.toolbar.selectedClass);
}
el.setAttribute("aria-pressed", String(ch.w.globals[enabledType]));
});
}
getToolbarIconsReference() {
const w = this.w;
if (!this.elZoom) {
this.elZoom = w.dom.baseEl.querySelector(".apexcharts-zoom-icon");
}
if (!this.elPan) {
this.elPan = w.dom.baseEl.querySelector(".apexcharts-pan-icon");
}
if (!this.elSelection) {
this.elSelection = w.dom.baseEl.querySelector(
".apexcharts-selection-icon"
);
}
}
/**
* @param {string} type
*/
enableZoomPanFromToolbar(type) {
this.toggleOtherControls();
type === "pan" ? this.w.interact.panEnabled = true : this.w.interact.zoomEnabled = true;
const el = type === "pan" ? this.elPan : this.elZoom;
const el2 = type === "pan" ? this.elZoom : this.elPan;
if (el) {
el.classList.add(this.selectedClass);
}
if (el2) {
el2.classList.remove(this.selectedClass);
}
}
togglePanning() {
const charts = this.ctx.getSyncedCharts();
charts.forEach((ch) => {
ch.ctx.toolbar.toggleOtherControls();
ch.w.interact.panEnabled = !ch.w.interact.panEnabled;
if (!ch.ctx.toolbar.elPan.classList.contains(ch.ctx.toolbar.selectedClass)) {
ch.ctx.toolbar.elPan.classList.add(ch.ctx.toolbar.selectedClass);
} else {
ch.ctx.toolbar.elPan.classList.remove(ch.ctx.toolbar.selectedClass);
}
ch.ctx.toolbar.elPan.setAttribute(
"aria-pressed",
String(ch.w.interact.panEnabled)
);
});
}
toggleOtherControls() {
const w = this.w;
w.interact.panEnabled = false;
w.interact.zoomEnabled = false;
w.interact.selectionEnabled = false;
this.getToolbarIconsReference();
const toggleEls = [this.elPan, this.elSelection, this.elZoom];
toggleEls.forEach((el) => {
if (el) {
el.classList.remove(this.selectedClass);
}
});
}
handleZoomIn() {
const w = this.w;
if (w.axisFlags.isRangeBar) {
this.minX = w.globals.minY;
this.maxX = w.globals.maxY;
}
const centerX = (this.minX + this.maxX) / 2;
const newMinX = (this.minX + centerX) / 2;
const newMaxX = (this.maxX + centerX) / 2;
const newMinXMaxX = this._getNewMinXMaxX(newMinX, newMaxX);
if (!w.interact.disableZoomIn) {
this.zoomUpdateOptions(newMinXMaxX.minX, newMinXMaxX.maxX);
}
}
handleZoomOut() {
const w = this.w;
if (w.axisFlags.isRangeBar) {
this.minX = w.globals.minY;
this.maxX = w.globals.maxY;
}
if (w.config.xaxis.type === "datetime" && new Date(this.minX).getUTCFullYear() < 1e3) {
return;
}
const centerX = (this.minX + this.maxX) / 2;
const newMinX = this.minX - (centerX - this.minX);
const newMaxX = this.maxX - (centerX - this.maxX);
const newMinXMaxX = this._getNewMinXMaxX(newMinX, newMaxX);
if (!w.interact.disableZoomOut) {
this.zoomUpdateOptions(newMinXMaxX.minX, newMinXMaxX.maxX);
}
}
/**
* @param {number} newMinX
* @param {number} newMaxX
*/
_getNewMinXMaxX(newMinX, newMaxX) {
const shouldFloor = this.w.config.xaxis.convertedCatToNumeric;
return {
minX: shouldFloor ? Math.floor(newMinX) : newMinX,
maxX: shouldFloor ? Math.floor(newMaxX) : newMaxX
};
}
/**
* @param {number} newMinX
* @param {number} newMaxX
*/
zoomUpdateOptions(newMinX, newMaxX) {
const w = this.w;
if (newMinX === void 0 && newMaxX === void 0) {
this.handleZoomReset();
return;
}
if (w.config.xaxis.convertedCatToNumeric) {
if (newMinX < 1) {
newMinX = 1;
newMaxX = w.globals.dataPoints;
}
if (newMaxX - newMinX < 2) {
return;
}
}
let xaxis = {
min: newMinX,
max: newMaxX
};
const beforeZoomRange = this.getBeforeZoomRange(
xaxis,
/** @type {any} */
void 0
);
if (beforeZoomRange) {
xaxis = beforeZoomRange.xaxis;
}
const options2 = {
xaxis
};
if (!w.globals.initialConfig) return;
const yaxis = Utils$1.clone(w.globals.initialConfig.yaxis);
if (!w.config.chart.group) {
options2.yaxis = yaxis;
}
this.w.interact.zoomed = true;
this.ctx.updateHelpers._updateOptions(
options2,
false,
this.w.config.chart.animations.dynamicAnimation.enabled
);
this.zoomCallback(xaxis, yaxis);
}
/**
* @param {Record<string, any>} xaxis
* @param {Record<string, any>} yaxis
*/
zoomCallback(xaxis, yaxis) {
if (typeof this.ev.zoomed === "function") {
this.ev.zoomed(this.ctx, { xaxis, yaxis });
this.ctx.events.fireEvent("zoomed", { xaxis, yaxis });
}
}
/**
* @param {Record<string, any>} xaxis
* @param {Record<string, any>} yaxis
*/
getBeforeZoomRange(xaxis, yaxis) {
let newRange = null;
if (typeof this.ev.beforeZoom === "function") {
newRange = this.ev.beforeZoom(this, { xaxis, yaxis });
}
return newRange;
}
toggleMenu() {
window.setTimeout(() => {
var _a, _b, _c;
if ((_a = this.elMenu) == null ? void 0 : _a.classList.contains("apexcharts-menu-open")) {
this._closeMenu();
} else {
(_b = this.elMenu) == null ? void 0 : _b.classList.add("apexcharts-menu-open");
(_c = this.elMenuIcon) == null ? void 0 : _c.setAttribute("aria-expanded", "true");
}
}, 0);
}
_closeMenu() {
var _a, _b;
(_a = this.elMenu) == null ? void 0 : _a.classList.remove("apexcharts-menu-open");
(_b = this.elMenuIcon) == null ? void 0 : _b.setAttribute("aria-expanded", "false");
}
/**
* @param {string} type
*/
handleDownload(type) {
const w = this.w;
const exprt = new Exports(this.w, this.ctx);
switch (type) {
case "svg":
exprt.exportToSVG();
break;
case "png":
exprt.exportToPng();
break;
case "csv":
exprt.exportToCSV({
series: w.config.series,
columnDelimiter: w.config.chart.toolbar.export.csv.columnDelimiter
});
break;
}
}
handleZoomReset() {
const charts = this.ctx.getSyncedCharts();
charts.forEach((ch) => {
const w = ch.w;
if (!w.interact.zoomed) return;
w.globals.lastXAxis.min = w.globals.initialConfig.xaxis.min;
w.globals.lastXAxis.max = w.globals.initialConfig.xaxis.max;
ch.updateHelpers.revertDefaultAxisMinMax();
if (typeof w.config.chart.events.beforeResetZoom === "function") {
const resetZoomRange = w.config.chart.events.beforeResetZoom(ch, w);
if (resetZoomRange) {
ch.updateHelpers.revertDefaultAxisMinMax(resetZoomRange);
}
}
if (typeof w.config.chart.events.zoomed === "function") {
ch.ctx.toolbar.zoomCallback({
min: w.config.xaxis.min,
max: w.config.xaxis.max
});
}
const series = ch.ctx.series.emptyCollapsedSeries(
Utils$1.clone(w.globals.initialSeries)
);
ch.updateHelpers._updateSeries(
series,
w.config.chart.animations.dynamicAnimation.enabled
);
w.interact.zoomed = false;
});
}
destroy() {
this.elZoom = null;
this.elZoomIn = null;
this.elZoomOut = null;
this.elPan = null;
this.elSelection = null;
this.elZoomReset = null;
this.elMenuIcon = null;
}
}
class ZoomPanSelection extends Toolbar {
/**
* @param {import('../types/internal').ChartStateW} w
* @param {import('../types/internal').ChartContext} ctx
*/
constructor(w, ctx) {
super(w, ctx);
this.w = w;
this.ctx = ctx;
this.dragged = false;
this.graphics = new Graphics(this.w);
this.eventList = [
"mousedown",
"mouseleave",
"mousemove",
"touchstart",
"touchmove",
"mouseup",
"touchend",
"wheel"
];
this.clientX = 0;
this.clientY = 0;
this.startX = 0;
this.endX = 0;
this.dragX = 0;
this.startY = 0;
this.endY = 0;
this.dragY = 0;
this.moveDirection = "none";
this.debounceTimer = null;
this.debounceDelay = 100;
this.wheelDelay = 400;
}
/** @param {{xyRatios: any}} opts */
init({ xyRatios }) {
const w = this.w;
const me = this;
this.xyRatios = xyRatios;
this.zoomRect = this.graphics.drawRect(0, 0, 0, 0);
this.selectionRect = this.graphics.drawRect(0, 0, 0, 0);
this.gridRect = w.dom.baseEl.querySelector(".apexcharts-grid");
this.constraints = new Box(0, 0, w.layout.gridWidth, w.layout.gridHeight);
this.zoomRect.node.classList.add("apexcharts-zoom-rect");
this.selectionRect.node.classList.add("apexcharts-selection-rect");
w.dom.Paper.add(this.zoomRect);
w.dom.Paper.add(this.selectionRect);
if (w.config.chart.selection.type === "x") {
this.slDraggableRect = this.selectionRect.draggable({
minX: 0,
minY: 0,
maxX: w.layout.gridWidth,
maxY: w.layout.gridHeight
}).on("dragmove.namespace", this.selectionDragging.bind(this, "dragging"));
} else if (w.config.chart.selection.type === "y") {
this.slDraggableRect = this.selectionRect.draggable({
minX: 0,
maxX: w.layout.gridWidth
}).on("dragmove.namespace", this.selectionDragging.bind(this, "dragging"));
} else {
this.slDraggableRect = this.selectionRect.draggable().on("dragmove.namespace", this.selectionDragging.bind(this, "dragging"));
}
this.preselectedSelection();
this.hoverArea = /** @type {Element} */
w.dom.baseEl.querySelector(`${w.globals.chartClass} .apexcharts-svg`);
if (!this.hoverArea) return;
this.hoverArea.classList.add("apexcharts-zoomable");
this.eventList.forEach((event) => {
var _a;
(_a = this.hoverArea) == null ? void 0 : _a.addEventListener(
event,
me.svgMouseEvents.bind(me, xyRatios),
{
capture: false,
passive: true
}
);
});
if (w.config.chart.zoom.enabled && w.config.chart.zoom.allowMouseWheelZoom) {
this.hoverArea.addEventListener("wheel", me.mouseWheelEvent.bind(me), {
capture: false,
passive: false
});
}
}
// remove the event listeners which were previously added on hover area
destroy() {
if (this.slDraggableRect) {
this.slDraggableRect.draggable(false);
this.slDraggableRect.off();
this.selectionRect.off();
}
this.selectionRect = null;
this.zoomRect = null;
this.gridRect = null;
}
/**
* @param {import('../types/internal').XYRatios} xyRatios
* @param {any} e
*/
svgMouseEvents(xyRatios, e) {
var _a;
const w = this.w;
const toolbar = this.ctx.toolbar;
const zoomtype = w.interact.zoomEnabled ? w.config.chart.zoom.type : w.config.chart.selection.type;
const autoSelected = w.config.chart.toolbar.autoSelected;
if (e.shiftKey) {
this.shiftWasPressed = true;
toolbar.enableZoomPanFromToolbar(autoSelected === "pan" ? "zoom" : "pan");
} else {
if (this.shiftWasPressed) {
toolbar.enableZoomPanFromToolbar(autoSelected);
this.shiftWasPressed = false;
}
}
if (!e.target) return;
const tc = e.target.classList;
let pc;
if (e.target.parentNode && e.target.parentNode !== null) {
pc = e.target.parentNode.classList;
}
const falsePositives = tc.contains("apexcharts-legend-marker") || tc.contains("apexcharts-legend-text") || pc && pc.contains("apexcharts-toolbar");
if (falsePositives) return;
this.clientX = e.type === "touchmove" || e.type === "touchstart" ? e.touches[0].clientX : e.type === "touchend" ? e.changedTouches[0].clientX : e.clientX;
this.clientY = e.type === "touchmove" || e.type === "touchstart" ? e.touches[0].clientY : e.type === "touchend" ? e.changedTouches[0].clientY : e.clientY;
if (e.type === "mousedown" && e.which === 1 || e.type === "touchstart") {
const gridRectDim = (_a = this.gridRect) == null ? void 0 : _a.getBoundingClientRect();
if (!gridRectDim) return;
this.startX = this.clientX - gridRectDim.left - w.globals.barPadForNumericAxis;
this.startY = this.clientY - gridRectDim.top;
this.dragged = false;
this.w.interact.mousedown = true;
}
if (e.type === "mousemove" && e.which === 1 || e.type === "touchmove") {
this.dragged = true;
if (w.interact.panEnabled) {
w.interact.selection = null;
if (this.w.interact.mousedown) {
this.panDragging({
context: this,
zoomtype,
xyRatios
});
}
} else {
if (this.w.interact.mousedown && w.interact.zoomEnabled || this.w.interact.mousedown && w.interact.selectionEnabled) {
this.selection = this.selectionDrawing({
context: this,
zoomtype
});
}
}
}
if (e.type === "mouseup" || e.type === "touchend" || e.type === "mouseleave") {
this.handleMouseUp({ zoomtype });
}
this.makeSelectionRectDraggable();
}
/** @param {{ zoomtype?: any, isResized?: any }} opts */
handleMouseUp({ zoomtype, isResized }) {
var _a;
const w = this.w;
const gridRectDim = (_a = this.gridRect) == null ? void 0 : _a.getBoundingClientRect();
if (gridRectDim && (this.w.interact.mousedown || isResized)) {
this.endX = this.clientX - gridRectDim.left - w.globals.barPadForNumericAxis;
this.endY = this.clientY - gridRectDim.top;
this.dragX = Math.abs(this.endX - this.startX);
this.dragY = Math.abs(this.endY - this.startY);
if (w.interact.zoomEnabled || w.interact.selectionEnabled) {
this.selectionDrawn({
context: this,
zoomtype
});
}
}
if (w.interact.zoomEnabled) {
this.hideSelectionRect(this.selectionRect);
}
this.dragged = false;
this.w.interact.mousedown = false;
}
/**
* @param {Event} e
*/
mouseWheelEvent(e) {
const w = this.w;
e.preventDefault();
const now = Date.now();
if (now - w.interact.lastWheelExecution > this.wheelDelay) {
this.executeMouseWheelZoom(e);
w.interact.lastWheelExecution = now;
}
if (this.debounceTimer) clearTimeout(this.debounceTimer);
this.debounceTimer = setTimeout(() => {
if (now - w.interact.lastWheelExecution > this.wheelDelay) {
this.executeMouseWheelZoom(e);
w.interact.lastWheelExecution = now;
}
}, this.debounceDelay);
}
/**
* @param {any} e
*/
executeMouseWheelZoom(e) {
var _a;
const w = this.w;
this.minX = w.axisFlags.isRangeBar ? w.globals.minY : w.globals.minX;
this.maxX = w.axisFlags.isRangeBar ? w.globals.maxY : w.globals.maxX;
const gridRectDim = (_a = this.gridRect) == null ? void 0 : _a.getBoundingClientRect();
if (!gridRectDim) return;
const mouseX = (e.clientX - gridRectDim.left) / gridRectDim.width;
const currentMinX = this.minX;
const currentMaxX = this.maxX;
const totalX = currentMaxX - currentMinX;
const zoomFactorIn = 0.5;
const zoomFactorOut = 1.5;
let zoomRange;
let newMinX, newMaxX;
if (e.deltaY < 0) {
zoomRange = zoomFactorIn * totalX;
const midPoint = currentMinX + mouseX * totalX;
newMinX = midPoint - zoomRange / 2;
newMaxX = midPoint + zoomRange / 2;
} else {
zoomRange = zoomFactorOut * totalX;
newMinX = currentMinX - zoomRange / 2;
newMaxX = currentMaxX + zoomRange / 2;
}
if (!w.axisFlags.isRangeBar) {
newMinX = Math.max(newMinX, w.globals.initialMinX);
newMaxX = Math.min(newMaxX, w.globals.initialMaxX);
const minRange = (w.globals.initialMaxX - w.globals.initialMinX) * 0.01;
if (newMaxX - newMinX < minRange) {
const midPoint = (newMinX + newMaxX) / 2;
newMinX = midPoint - minRange / 2;
newMaxX = midPoint + minRange / 2;
}
}
const newMinXMaxX = this._getNewMinXMaxX(newMinX, newMaxX);
if (!isNaN(newMinXMaxX.minX) && !isNaN(newMinXMaxX.maxX)) {
this.zoomUpdateOptions(newMinXMaxX.minX, newMinXMaxX.maxX);
}
}
makeSelectionRectDraggable() {
const w = this.w;
if (!this.selectionRect) return;
const rectDim = this.selectionRect.node.getBoundingClientRect();
if (rectDim.width > 0 && rectDim.height > 0) {
this.selectionRect.select(false).resize(false);
this.selectionRect.select({
createRot: () => {
},
updateRot: () => {
},
createHandle: (group, p, index, pointArr, handleName) => {
if (handleName === "l" || handleName === "r")
return group.circle(8).css({ "stroke-width": 1, stroke: "#333", fill: "#fff" });
return group.circle(0);
},
updateHandle: (group, p) => {
return group.center(p[0], p[1]);
}
}).resize().on("resize", () => {
const zoomtype = w.interact.zoomEnabled ? w.config.chart.zoom.type : w.config.chart.selection.type;
this.handleMouseUp({ zoomtype, isResized: true });
});
}
}
preselectedSelection() {
const w = this.w;
const xyRatios = this.xyRatios;
if (!w.interact.zoomEnabled) {
if (typeof w.interact.selection !== "undefined" && w.interact.selection !== null) {
this.drawSelectionRect(__spreadProps(__spreadValues({}, w.interact.selection), {
translateX: w.layout.translateX,
translateY: w.layout.translateY
}));
} else {
if (w.config.chart.selection.xaxis.min !== void 0 && w.config.chart.selection.xaxis.max !== void 0) {
let x = (w.config.chart.selection.xaxis.min - w.globals.minX) / xyRatios.xRatio;
let width = w.layout.gridWidth - (w.globals.maxX - w.config.chart.selection.xaxis.max) / xyRatios.xRatio - x;
if (w.axisFlags.isRangeBar) {
x = (w.config.chart.selection.xaxis.min - w.globals.yAxisScale[0].niceMin) / xyRatios.invertedYRatio;
width = (w.config.chart.selection.xaxis.max - w.config.chart.selection.xaxis.min) / xyRatios.invertedYRatio;
}
const selectionRect = {
x,
y: 0,
width,
height: w.layout.gridHeight,
translateX: w.layout.translateX,
translateY: w.layout.translateY,
selectionEnabled: true
};
this.drawSelectionRect(selectionRect);
this.makeSelectionRectDraggable();
if (typeof w.config.chart.events.selection === "function") {
w.config.chart.events.selection(this.ctx, {
xaxis: {
min: w.config.chart.selection.xaxis.min,
max: w.config.chart.selection.xaxis.max
},
yaxis: {}
});
}
}
}
}
}
/** @param {{x: any, y: any, width: any, height: any, translateX: any, translateY: any}} opts */
drawSelectionRect({ x, y, width, height, translateX = 0, translateY = 0 }) {
const w = this.w;
const zoomRect = this.zoomRect;
const selectionRect = this.selectionRect;
if (this.dragged || w.interact.selection !== null) {
const scalingAttrs = {
transform: "translate(" + translateX + ", " + translateY + ")"
};
if (w.interact.zoomEnabled && this.dragged) {
if (width < 0) width = 1;
zoomRect.attr({
x,
y,
width,
height,
fill: w.config.chart.zoom.zoomedArea.fill.color,
"fill-opacity": w.config.chart.zoom.zoomedArea.fill.opacity,
stroke: w.config.chart.zoom.zoomedArea.stroke.color,
"stroke-width": w.config.chart.zoom.zoomedArea.stroke.width,
"stroke-opacity": w.config.chart.zoom.zoomedArea.stroke.opacity
});
Graphics.setAttrs(zoomRect.node, scalingAttrs);
}
if (w.interact.selectionEnabled) {
selectionRect.attr({
x,
y,
width: width > 0 ? width : 0,
height: height > 0 ? height : 0,
fill: w.config.chart.selection.fill.color,
"fill-opacity": w.config.chart.selection.fill.opacity,
stroke: w.config.chart.selection.stroke.color,
"stroke-width": w.config.chart.selection.stroke.width,
"stroke-dasharray": w.config.chart.selection.stroke.dashArray,
"stroke-opacity": w.config.chart.selection.stroke.opacity
});
Graphics.setAttrs(selectionRect.node, scalingAttrs);
}
}
}
/**
* @param {any} rect
*/
hideSelectionRect(rect) {
if (rect) {
rect.attr({
x: 0,
y: 0,
width: 0,
height: 0
});
}
}
selectionDrawing({ context, zoomtype }) {
var _a;
const w = this.w;
const me = context;
const gridRectDim = (_a = this.gridRect) == null ? void 0 : _a.getBoundingClientRect();
if (!gridRectDim) return;
const startX = me.startX - 1;
const startY = me.startY;
let inversedX = false;
let inversedY = false;
const left = me.clientX - gridRectDim.left - w.globals.barPadForNumericAxis;
const top = me.clientY - gridRectDim.top;
let selectionWidth = left - startX;
let selectionHeight = top - startY;
let selectionRect = {
translateX: w.layout.translateX,
translateY: w.layout.translateY
};
if (Math.abs(selectionWidth + startX) > w.layout.gridWidth) {
selectionWidth = w.layout.gridWidth - startX;
} else if (left < 0) {
selectionWidth = startX;
}
if (startX > left) {
inversedX = true;
selectionWidth = Math.abs(selectionWidth);
}
if (startY > top) {
inversedY = true;
selectionHeight = Math.abs(selectionHeight);
}
if (zoomtype === "x") {
selectionRect = {
x: inversedX ? startX - selectionWidth : startX,
y: 0,
width: selectionWidth,
height: w.layout.gridHeight
};
} else if (zoomtype === "y") {
selectionRect = {
x: 0,
y: inversedY ? startY - selectionHeight : startY,
width: w.layout.gridWidth,
height: selectionHeight
};
} else {
selectionRect = {
x: inversedX ? startX - selectionWidth : startX,
y: inversedY ? startY - selectionHeight : startY,
width: selectionWidth,
height: selectionHeight
};
}
selectionRect = __spreadProps(__spreadValues({}, selectionRect), {
translateX: w.layout.translateX,
translateY: w.layout.translateY
});
me.drawSelectionRect(selectionRect);
me.selectionDragging("resizing");
return selectionRect;
}
/**
* @param {string} type
* @param {CustomEvent} e
*/
selectionDragging(type, e) {
var _a;
const w = this.w;
if (!e) return;
e.preventDefault();
const { handler, box } = e.detail;
const constraints = (
/** @type {any} */
this.constraints
);
let { x, y } = box;
if (x < constraints.x) {
x = constraints.x;
}
if (y < constraints.y) {
y = constraints.y;
}
if (box.x2 > constraints.x2) {
x = constraints.x2 - box.w;
}
if (box.y2 > constraints.y2) {
y = constraints.y2 - box.h;
}
handler.move(x, y);
const xyRatios = this.xyRatios;
const selRect = this.selectionRect;
let timerInterval = 0;
if (type === "resizing") {
timerInterval = 30;
}
const getSelAttr = (attr) => {
return parseFloat(selRect.node.getAttribute(attr));
};
const draggedProps = {
x: getSelAttr("x"),
y: getSelAttr("y"),
width: getSelAttr("width"),
height: getSelAttr("height")
};
w.interact.selection = draggedProps;
if (typeof w.config.chart.events.selection === "function" && w.interact.selectionEnabled) {
clearTimeout((_a = this.w.globals.selectionResizeTimer) != null ? _a : void 0);
this.w.globals.selectionResizeTimer = window.setTimeout(() => {
var _a2;
const gridRectDim = (_a2 = this.gridRect) == null ? void 0 : _a2.getBoundingClientRect();
if (!gridRectDim) return;
const selectionRect = selRect.node.getBoundingClientRect();
let minX, maxX, minY, maxY;
if (!w.axisFlags.isRangeBar) {
if (!w.globals.xAxisScale) return;
minX = w.globals.xAxisScale.niceMin + (selectionRect.left - gridRectDim.left) * xyRatios.xRatio;
maxX = w.globals.xAxisScale.niceMin + (selectionRect.right - gridRectDim.left) * xyRatios.xRatio;
minY = w.globals.yAxisScale[0].niceMin + (gridRectDim.bottom - selectionRect.bottom) * xyRatios.yRatio[0];
maxY = w.globals.yAxisScale[0].niceMax - (selectionRect.top - gridRectDim.top) * xyRatios.yRatio[0];
} else {
minX = w.globals.yAxisScale[0].niceMin + (selectionRect.left - gridRectDim.left) * xyRatios.invertedYRatio;
maxX = w.globals.yAxisScale[0].niceMin + (selectionRect.right - gridRectDim.left) * xyRatios.invertedYRatio;
minY = 0;
maxY = 1;
}
const xyAxis = {
xaxis: {
min: minX,
max: maxX
},
yaxis: {
min: minY,
max: maxY
}
};
w.config.chart.events.selection(this.ctx, xyAxis);
if (w.config.chart.brush.enabled && w.config.chart.events.brushScrolled !== void 0) {
w.config.chart.events.brushScrolled(this.ctx, xyAxis);
}
}, timerInterval);
}
}
/** @param {{context: any, zoomtype: any}} opts */
selectionDrawn({ context, zoomtype }) {
var _a, _b;
const w = this.w;
const me = context;
const xyRatios = this.xyRatios;
const toolbar = this.ctx.toolbar;
const selRect = w.interact.zoomEnabled ? me.zoomRect.node.getBoundingClientRect() : me.selectionRect.node.getBoundingClientRect();
const gridRectDim = me.gridRect.getBoundingClientRect();
const localStartX = selRect.left - gridRectDim.left - w.globals.barPadForNumericAxis;
const localEndX = selRect.right - gridRectDim.left - w.globals.barPadForNumericAxis;
const localStartY = selRect.top - gridRectDim.top;
const localEndY = selRect.bottom - gridRectDim.top;
let xLowestValue, xHighestValue;
if (!w.axisFlags.isRangeBar) {
const niceMin = (_b = (_a = w.globals.xAxisScale) == null ? void 0 : _a.niceMin) != null ? _b : 0;
xLowestValue = niceMin + localStartX * xyRatios.xRatio;
xHighestValue = niceMin + localEndX * xyRatios.xRatio;
} else {
xLowestValue = w.globals.yAxisScale[0].niceMin + localStartX * xyRatios.invertedYRatio;
xHighestValue = w.globals.yAxisScale[0].niceMin + localEndX * xyRatios.invertedYRatio;
}
const yHighestValue = [];
const yLowestValue = [];
w.config.yaxis.forEach((yaxe, index) => {
const seriesIndex = w.globals.seriesYAxisMap[index][0];
const highestVal = w.globals.yAxisScale[index].niceMax - xyRatios.yRatio[seriesIndex] * localStartY;
const lowestVal = w.globals.yAxisScale[index].niceMax - xyRatios.yRatio[seriesIndex] * localEndY;
yHighestValue.push(highestVal);
yLowestValue.push(lowestVal);
});
if (me.dragged && (me.dragX > 10 || me.dragY > 10) && xLowestValue !== xHighestValue) {
if (w.interact.zoomEnabled) {
if (!w.globals.initialConfig) return;
let yaxis = Utils$1.clone(w.globals.initialConfig.yaxis);
let xaxis = Utils$1.clone(w.globals.initialConfig.xaxis);
w.interact.zoomed = true;
if (w.config.xaxis.convertedCatToNumeric) {
xLowestValue = Math.floor(xLowestValue);
xHighestValue = Math.floor(xHighestValue);
if (xLowestValue < 1) {
xLowestValue = 1;
xHighestValue = w.globals.dataPoints;
}
if (xHighestValue - xLowestValue < 2) {
xHighestValue = xLowestValue + 1;
}
}
if (zoomtype === "xy" || zoomtype === "x") {
xaxis = {
min: xLowestValue,
max: xHighestValue
};
}
if (zoomtype === "xy" || zoomtype === "y") {
yaxis.forEach((yaxe, index) => {
yaxis[index].min = yLowestValue[index];
yaxis[index].max = yHighestValue[index];
});
}
if (toolbar) {
const beforeZoomRange = toolbar.getBeforeZoomRange(xaxis, yaxis);
if (beforeZoomRange) {
xaxis = beforeZoomRange.xaxis ? beforeZoomRange.xaxis : xaxis;
yaxis = beforeZoomRange.yaxis ? beforeZoomRange.yaxis : yaxis;
}
}
const options2 = {
xaxis
};
if (!w.config.chart.group) {
options2.yaxis = yaxis;
}
me.ctx.updateHelpers._updateOptions(
options2,
false,
me.w.config.chart.animations.dynamicAnimation.enabled
);
if (typeof w.config.chart.events.zoomed === "function") {
toolbar.zoomCallback(xaxis, yaxis);
}
} else if (w.interact.selectionEnabled) {
let yaxis = null;
let xaxis = null;
xaxis = {
min: xLowestValue,
max: xHighestValue
};
if (zoomtype === "xy" || zoomtype === "y") {
const yaxisCopy = (
/** @type {ApexYAxis[]} */
Utils$1.clone(w.config.yaxis)
);
yaxis = yaxisCopy;
yaxisCopy.forEach((yaxe, index) => {
yaxisCopy[index].min = yLowestValue[index];
yaxisCopy[index].max = yHighestValue[index];
});
}
w.interact.selection = me.selection;
if (typeof w.config.chart.events.selection === "function") {
w.config.chart.events.selection(me.ctx, {
xaxis,
yaxis
});
}
}
}
}
/** @param {{ context?: any, zoomtype?: any, xyRatios?: any }} opts */
panDragging({ context }) {
var _a;
const w = this.w;
const me = context;
if (typeof w.interact.lastClientPosition.x !== "undefined") {
const deltaX = w.interact.lastClientPosition.x - me.clientX;
const deltaY = ((_a = w.interact.lastClientPosition.y) != null ? _a : 0) - me.clientY;
if (Math.abs(deltaX) > Math.abs(deltaY) && deltaX > 0) {
this.moveDirection = "left";
} else if (Math.abs(deltaX) > Math.abs(deltaY) && deltaX < 0) {
this.moveDirection = "right";
} else if (Math.abs(deltaY) > Math.abs(deltaX) && deltaY > 0) {
this.moveDirection = "up";
} else if (Math.abs(deltaY) > Math.abs(deltaX) && deltaY < 0) {
this.moveDirection = "down";
}
}
w.interact.lastClientPosition = {
x: me.clientX,
y: me.clientY
};
const xLowestValue = w.axisFlags.isRangeBar ? w.globals.minY : w.globals.minX;
const xHighestValue = w.axisFlags.isRangeBar ? w.globals.maxY : w.globals.maxX;
me.panScrolled(xLowestValue, xHighestValue);
}
// delayedPanScrolled() {
// const w = this.w
// let newMinX = w.globals.minX
// let newMaxX = w.globals.maxX
// const centerX = (w.globals.maxX - w.globals.minX) / 2
// if (this.moveDirection === 'left') {
// newMinX = w.globals.minX + centerX
// newMaxX = w.globals.maxX + centerX
// } else if (this.moveDirection === 'right') {
// newMinX = w.globals.minX - centerX
// newMaxX = w.globals.maxX - centerX
// }
// newMinX = Math.floor(newMinX)
// newMaxX = Math.floor(newMaxX)
// this.updateScrolledChart(
// { xaxis: { min: newMinX, max: newMaxX } },
// newMinX,
// newMaxX
// )
// }
/**
* @param {number} xLowestValue
* @param {number} xHighestValue
*/
panScrolled(xLowestValue, xHighestValue) {
const w = this.w;
const xyRatios = this.xyRatios;
if (!w.globals.initialConfig) return;
const yaxis = Utils$1.clone(w.globals.initialConfig.yaxis);
let xRatio = xyRatios.xRatio;
let minX = w.globals.minX;
let maxX = w.globals.maxX;
if (w.axisFlags.isRangeBar) {
xRatio = xyRatios.invertedYRatio;
minX = w.globals.minY;
maxX = w.globals.maxY;
}
if (this.moveDirection === "left") {
xLowestValue = minX + w.layout.gridWidth / 15 * xRatio;
xHighestValue = maxX + w.layout.gridWidth / 15 * xRatio;
} else if (this.moveDirection === "right") {
xLowestValue = minX - w.layout.gridWidth / 15 * xRatio;
xHighestValue = maxX - w.layout.gridWidth / 15 * xRatio;
}
if (!w.axisFlags.isRangeBar) {
if (xLowestValue < w.globals.initialMinX || xHighestValue > w.globals.initialMaxX) {
xLowestValue = minX;
xHighestValue = maxX;
}
}
const xaxis = {
min: xLowestValue,
max: xHighestValue
};
const options2 = {
xaxis
};
if (!w.config.chart.group) {
options2.yaxis = yaxis;
}
this.updateScrolledChart(options2, xLowestValue, xHighestValue);
}
/**
* @param {object} options
* @param {number} xLowestValue
* @param {number} xHighestValue
*/
updateScrolledChart(options2, xLowestValue, xHighestValue) {
const w = this.w;
this.ctx.updateHelpers._updateOptions(options2, false, false);
if (typeof w.config.chart.events.scrolled === "function") {
const args = {
xaxis: {
min: xLowestValue,
max: xHighestValue
}
};
w.config.chart.events.scrolled(this.ctx, args);
this.ctx.events.fireEvent("scrolled", args);
}
}
}
ApexCharts.registerFeatures({
toolbar: Toolbar,
zoomPanSelection: ZoomPanSelection
});
let Helpers$2 = class Helpers3 {
/**
* @param {import('./Annotations').default} annoCtx
*/
constructor(annoCtx) {
this.w = annoCtx.w;
this.annoCtx = annoCtx;
}
/**
* @param {Record<string, any>} anno
* @param {number | null} [annoIndex]
*/
setOrientations(anno, annoIndex = null) {
var _a, _b;
const w = this.w;
if (anno.label.orientation === "vertical") {
const i = annoIndex !== null ? annoIndex : 0;
const xAnno = w.dom.baseEl.querySelector(
`.apexcharts-xaxis-annotations .apexcharts-xaxis-annotation-label[rel='${i}']`
);
if (xAnno !== null) {
const xAnnoCoord = (
/** @type {SVGGraphicsElement} */
xAnno.getBBox()
);
xAnno.setAttribute(
"x",
String(
parseFloat((_a = xAnno.getAttribute("x")) != null ? _a : "0") - xAnnoCoord.height + 4
)
);
const yOffset = anno.label.position === "top" ? xAnnoCoord.width : -xAnnoCoord.width;
xAnno.setAttribute(
"y",
String(parseFloat((_b = xAnno.getAttribute("y")) != null ? _b : "0") + yOffset)
);
const { x, y } = this.annoCtx.graphics.rotateAroundCenter(xAnno);
xAnno.setAttribute("transform", `rotate(-90 ${x} ${y})`);
}
}
}
/**
* @param {any} annoEl
* @param {Record<string, any>} anno
*/
addBackgroundToAnno(annoEl, anno) {
const w = this.w;
if (!annoEl || !anno.label.text || !String(anno.label.text).trim()) {
return null;
}
const gridEl = w.dom.baseEl.querySelector(".apexcharts-grid");
if (!gridEl) return null;
const elGridRect = gridEl.getBoundingClientRect();
const gridBBox = (
/** @type {SVGGraphicsElement} */
gridEl.getBBox()
);
const zoom = elGridRect.width / gridBBox.width || 1;
const coords = annoEl.getBoundingClientRect();
let {
left: pleft,
right: pright,
top: ptop,
bottom: pbottom
} = anno.label.style.padding;
if (anno.label.orientation === "vertical") {
[ptop, pbottom, pleft, pright] = [pleft, pright, ptop, pbottom];
}
const x1 = (coords.left - elGridRect.left) / zoom - pleft;
const y1 = (coords.top - elGridRect.top) / zoom - ptop;
const elRect = this.annoCtx.graphics.drawRect(
x1 - w.globals.barPadForNumericAxis,
y1,
coords.width / zoom + pleft + pright,
coords.height / zoom + ptop + pbottom,
anno.label.borderRadius,
anno.label.style.background,
1,
anno.label.borderWidth,
anno.label.borderColor,
0
);
if (anno.id) {
elRect.node.classList.add(anno.id);
}
return elRect;
}
annotationsBackground() {
const w = this.w;
const add = (anno, i, type) => {
const annoLabel = w.dom.baseEl.querySelector(
`.apexcharts-${type}-annotations .apexcharts-${type}-annotation-label[rel='${i}']`
);
if (annoLabel) {
const parent = annoLabel.parentNode;
const elRect = this.addBackgroundToAnno(annoLabel, anno);
if (elRect) {
parent == null ? void 0 : parent.insertBefore(elRect.node, annoLabel);
if (anno.label.mouseEnter) {
elRect.node.addEventListener(
"mouseenter",
anno.label.mouseEnter.bind(this, anno)
);
}
if (anno.label.mouseLeave) {
elRect.node.addEventListener(
"mouseleave",
anno.label.mouseLeave.bind(this, anno)
);
}
if (anno.label.click) {
elRect.node.addEventListener(
"click",
anno.label.click.bind(this, anno)
);
}
}
}
};
w.config.annotations.xaxis.forEach(
(anno, i) => add(anno, i, "xaxis")
);
w.config.annotations.yaxis.forEach(
(anno, i) => add(anno, i, "yaxis")
);
w.config.annotations.points.forEach(
(anno, i) => add(anno, i, "point")
);
}
/**
* @param {string} type
* @param {Record<string, any>} anno
*/
getY1Y2(type, anno) {
var _a, _b;
const w = this.w;
const y = type === "y1" ? anno.y : anno.y2;
let yP;
let clipped = false;
if (this.annoCtx.invertAxis) {
const labels = w.config.xaxis.convertedCatToNumeric ? w.labelData.categoryLabels : w.labelData.labels;
const catIndex = labels.indexOf(y);
const xLabel = w.dom.baseEl.querySelector(
`.apexcharts-yaxis-texts-g text:nth-child(${catIndex + 1})`
);
yP = xLabel ? parseFloat((_a = xLabel.getAttribute("y")) != null ? _a : "0") : (w.layout.gridHeight / labels.length - 1) * (catIndex + 1) - w.globals.barHeight;
if (anno.seriesIndex !== void 0 && w.globals.barHeight) {
yP -= w.globals.barHeight / 2 * (w.seriesData.series.length - 1) - w.globals.barHeight * anno.seriesIndex;
}
} else {
const seriesIndex = w.globals.seriesYAxisMap[anno.yAxisIndex][0];
const yPos = w.config.yaxis[anno.yAxisIndex].logarithmic ? new CoreUtils(this.w).getLogVal(
w.config.yaxis[anno.yAxisIndex].logBase,
y,
seriesIndex
) / /** @type {any} */
w.globals.yLogRatio[seriesIndex] : (y - w.globals.minYArr[seriesIndex]) / (w.globals.yRange[seriesIndex] / w.layout.gridHeight);
yP = w.layout.gridHeight - Math.min(Math.max(yPos, 0), w.layout.gridHeight);
clipped = yPos > w.layout.gridHeight || yPos < 0;
if (anno.marker && (anno.y === void 0 || anno.y === null)) {
yP = 0;
}
if ((_b = w.config.yaxis[anno.yAxisIndex]) == null ? void 0 : _b.reversed) {
yP = yPos;
}
}
if (typeof y === "string" && y.includes("px")) {
yP = parseFloat(y);
}
return { yP, clipped };
}
/**
* @param {string} type
* @param {Record<string, any>} anno
*/
getX1X2(type, anno) {
const w = this.w;
const x = type === "x1" ? anno.x : anno.x2;
const min = this.annoCtx.invertAxis ? w.globals.minY : w.globals.minX;
const max = this.annoCtx.invertAxis ? w.globals.maxY : w.globals.maxX;
const range = this.annoCtx.invertAxis ? w.globals.yRange[0] : w.globals.xRange;
let clipped = false;
let xP = this.annoCtx.inversedReversedAxis ? (max - x) / (range / w.layout.gridWidth) : (x - min) / (range / w.layout.gridWidth);
if ((w.config.xaxis.type === "category" || w.config.xaxis.convertedCatToNumeric) && !this.annoCtx.invertAxis && !w.axisFlags.dataFormatXNumeric) {
if (!w.config.chart.sparkline.enabled) {
xP = this.getStringX(x);
}
}
if (typeof x === "string" && x.includes("px")) {
xP = parseFloat(x);
}
if ((x === void 0 || x === null) && anno.marker) {
xP = w.layout.gridWidth;
}
if (anno.seriesIndex !== void 0 && w.globals.barWidth && !this.annoCtx.invertAxis) {
xP -= w.globals.barWidth / 2 * (w.seriesData.series.length - 1) - w.globals.barWidth * anno.seriesIndex;
}
if (typeof xP !== "number") {
xP = 0;
clipped = true;
}
if (parseFloat(xP.toFixed(10)) > parseFloat(w.layout.gridWidth.toFixed(10))) {
xP = w.layout.gridWidth;
clipped = true;
} else if (xP < 0) {
xP = 0;
clipped = true;
}
return { x: xP, clipped };
}
/**
* @param {number} x
*/
getStringX(x) {
var _a;
const w = this.w;
let rX = x;
if (w.config.xaxis.convertedCatToNumeric && w.labelData.categoryLabels.length) {
const strX = String(x);
x = w.labelData.categoryLabels.findIndex(
(l) => String(l) === strX
) + 1;
}
const catIndex = w.labelData.labels.map(
(item) => Array.isArray(item) ? item.join(" ") : item
).indexOf(x);
const xLabel = w.dom.baseEl.querySelector(
`.apexcharts-xaxis-texts-g text:nth-child(${catIndex + 1})`
);
if (xLabel) {
rX = parseFloat((_a = xLabel.getAttribute("x")) != null ? _a : "0");
}
return rX;
}
};
class XAnnotations {
/**
* @param {import('./Annotations').default} annoCtx
*/
constructor(annoCtx) {
this.w = annoCtx.w;
this.annoCtx = annoCtx;
this.invertAxis = this.annoCtx.invertAxis;
this.helpers = new Helpers$2(this.annoCtx);
}
/**
* @param {XAxisAnnotations} anno
* @param {Element} parent
* @param {number} index
*/
addXaxisAnnotation(anno, parent, index) {
const w = this.w;
const result = this.helpers.getX1X2("x1", anno);
let x1 = result.x;
const clipX1 = result.clipped;
let clipX2 = true;
let x2;
const text = anno.label.text;
const strokeDashArray = anno.strokeDashArray;
if (!Utils$1.isNumber(x1)) return;
if (anno.x2 === null || typeof anno.x2 === "undefined") {
if (!clipX1) {
const line = this.annoCtx.graphics.drawLine(
x1 + anno.offsetX,
// x1
0 + anno.offsetY,
// y1
x1 + anno.offsetX,
// x2
w.layout.gridHeight + anno.offsetY,
// y2
anno.borderColor,
// lineColor
strokeDashArray,
//dashArray
anno.borderWidth
);
parent.appendChild(line.node);
if (anno.id) {
line.node.classList.add(anno.id);
}
}
} else {
const result2 = this.helpers.getX1X2("x2", anno);
x2 = result2.x;
clipX2 = result2.clipped;
if (x2 < x1) {
const temp = x1;
x1 = x2;
x2 = temp;
}
const rect = this.annoCtx.graphics.drawRect(
x1 + anno.offsetX,
// x1
0 + anno.offsetY,
// y1
x2 - x1,
// x2
w.layout.gridHeight + anno.offsetY,
// y2
0,
// radius
anno.fillColor,
// color
anno.opacity,
// opacity,
1,
// strokeWidth
anno.borderColor,
// strokeColor
strokeDashArray
// stokeDashArray
);
rect.node.classList.add("apexcharts-annotation-rect");
rect.attr("clip-path", `url(#gridRectMask${w.globals.cuid})`);
parent.appendChild(rect.node);
if (anno.id) {
rect.node.classList.add(anno.id);
}
}
if (!(clipX1 && clipX2)) {
const textRects = this.annoCtx.graphics.getTextRects(
text,
anno.label.style.fontSize
);
const textY = anno.label.position === "top" ? 4 : anno.label.position === "center" ? w.layout.gridHeight / 2 + (anno.label.orientation === "vertical" ? textRects.width / 2 : 0) : w.layout.gridHeight;
const elText = this.annoCtx.graphics.drawText({
x: x1 + anno.label.offsetX,
y: textY + anno.label.offsetY - (anno.label.orientation === "vertical" ? anno.label.position === "top" ? textRects.width / 2 - 12 : -textRects.width / 2 : 0),
text,
textAnchor: anno.label.textAnchor,
fontSize: anno.label.style.fontSize,
fontFamily: anno.label.style.fontFamily,
fontWeight: anno.label.style.fontWeight,
foreColor: anno.label.style.color,
cssClass: `apexcharts-xaxis-annotation-label ${anno.label.style.cssClass} ${anno.id ? anno.id : ""}`
});
elText.attr({
rel: index
});
parent.appendChild(elText.node);
this.annoCtx.helpers.setOrientations(anno, index);
}
}
drawXAxisAnnotations() {
const w = this.w;
const elg = this.annoCtx.graphics.group({
class: "apexcharts-xaxis-annotations"
});
w.config.annotations.xaxis.map(
(anno, index) => {
this.addXaxisAnnotation(anno, elg.node, index);
}
);
return elg;
}
}
class YAnnotations {
/**
* @param {import('./Annotations').default} annoCtx
*/
constructor(annoCtx) {
this.w = annoCtx.w;
this.annoCtx = annoCtx;
this.helpers = new Helpers$2(this.annoCtx);
this.axesUtils = new AxesUtils(this.annoCtx.w, {
theme: this.annoCtx.theme,
timeScale: this.annoCtx.timeScale
});
}
/**
* @param {YAxisAnnotations} anno
* @param {Element} parent
* @param {number} index
*/
addYaxisAnnotation(anno, parent, index) {
const w = this.w;
const strokeDashArray = anno.strokeDashArray;
let result = this.helpers.getY1Y2("y1", anno);
let y1 = result.yP;
const clipY1 = result.clipped;
let y2;
let clipY2 = true;
let drawn = false;
const text = anno.label.text;
if (anno.y2 === null || typeof anno.y2 === "undefined") {
if (!clipY1) {
drawn = true;
const line = this.annoCtx.graphics.drawLine(
0 + anno.offsetX,
// x1
y1 + anno.offsetY,
// y1
this._getYAxisAnnotationWidth(anno),
// x2
y1 + anno.offsetY,
// y2
anno.borderColor,
// lineColor
strokeDashArray,
// dashArray
anno.borderWidth
);
parent.appendChild(line.node);
if (anno.id) {
line.node.classList.add(anno.id);
}
}
} else {
result = this.helpers.getY1Y2("y2", anno);
y2 = result.yP;
clipY2 = result.clipped;
if (y2 > y1) {
const temp = y1;
y1 = y2;
y2 = temp;
}
if (!(clipY1 && clipY2)) {
drawn = true;
const rect = this.annoCtx.graphics.drawRect(
0 + anno.offsetX,
// x1
y2 + anno.offsetY,
// y1
this._getYAxisAnnotationWidth(anno),
// x2
y1 - y2,
// y2
0,
// radius
anno.fillColor,
// color
anno.opacity,
// opacity,
1,
// strokeWidth
anno.borderColor,
// strokeColor
strokeDashArray
// stokeDashArray
);
rect.node.classList.add("apexcharts-annotation-rect");
rect.attr("clip-path", `url(#gridRectMask${w.globals.cuid})`);
parent.appendChild(rect.node);
if (anno.id) {
rect.node.classList.add(anno.id);
}
}
}
if (drawn) {
const textX = anno.label.position === "right" ? w.layout.gridWidth : anno.label.position === "center" ? w.layout.gridWidth / 2 : 0;
const elText = this.annoCtx.graphics.drawText({
x: textX + anno.label.offsetX,
y: (y2 != null ? y2 : y1) + anno.label.offsetY - 3,
text,
textAnchor: anno.label.textAnchor,
fontSize: anno.label.style.fontSize,
fontFamily: anno.label.style.fontFamily,
fontWeight: anno.label.style.fontWeight,
foreColor: anno.label.style.color,
cssClass: `apexcharts-yaxis-annotation-label ${anno.label.style.cssClass} ${anno.id ? anno.id : ""}`
});
elText.attr({
rel: index
});
parent.appendChild(elText.node);
}
}
/**
* @param {YAxisAnnotations} anno
*/
_getYAxisAnnotationWidth(anno) {
const w = this.w;
let width = w.layout.gridWidth;
if (anno.width.indexOf("%") > -1) {
width = w.layout.gridWidth * parseInt(anno.width, 10) / 100;
} else {
width = parseInt(anno.width, 10);
}
return width + anno.offsetX;
}
drawYAxisAnnotations() {
const w = this.w;
const elg = this.annoCtx.graphics.group({
class: "apexcharts-yaxis-annotations"
});
w.config.annotations.yaxis.forEach(
(anno, index) => {
anno.yAxisIndex = this.axesUtils.translateYAxisIndex(anno.yAxisIndex);
if (!(this.axesUtils.isYAxisHidden(anno.yAxisIndex) && this.axesUtils.yAxisAllSeriesCollapsed(anno.yAxisIndex))) {
this.addYaxisAnnotation(anno, elg.node, index);
}
}
);
return elg;
}
}
class PointAnnotations {
/**
* @param {import('./Annotations').default} annoCtx
*/
constructor(annoCtx) {
this.w = annoCtx.w;
this.annoCtx = annoCtx;
this.helpers = new Helpers$2(this.annoCtx);
}
/**
* @param {Record<string, any>} anno
* @param {Element} parent
* @param {number} index
*/
addPointAnnotation(anno, parent, index) {
const w = this.w;
if (w.globals.collapsedSeriesIndices.indexOf(anno.seriesIndex) > -1) {
return;
}
const resultX = this.helpers.getX1X2("x1", anno);
const x = resultX.x;
const clipX = resultX.clipped;
const resultY = this.helpers.getY1Y2("y1", anno);
const y = resultY.yP;
const clipY = resultY.clipped;
if (!Utils$1.isNumber(x)) return;
if (!(clipY || clipX)) {
const optsPoints = {
pSize: anno.marker.size,
pointStrokeWidth: anno.marker.strokeWidth,
pointFillColor: anno.marker.fillColor,
pointStrokeColor: anno.marker.strokeColor,
shape: anno.marker.shape,
pRadius: anno.marker.radius,
class: `apexcharts-point-annotation-marker ${anno.marker.cssClass} ${anno.id ? anno.id : ""}`
};
let point = this.annoCtx.graphics.drawMarker(
x + anno.marker.offsetX,
y + anno.marker.offsetY,
optsPoints
);
parent.appendChild(point.node);
const text = anno.label.text ? anno.label.text : "";
const elText = this.annoCtx.graphics.drawText({
x: x + anno.label.offsetX,
y: y + anno.label.offsetY - anno.marker.size - parseFloat(anno.label.style.fontSize) / 1.6,
text,
textAnchor: anno.label.textAnchor,
fontSize: anno.label.style.fontSize,
fontFamily: anno.label.style.fontFamily,
fontWeight: anno.label.style.fontWeight,
foreColor: anno.label.style.color,
cssClass: `apexcharts-point-annotation-label ${anno.label.style.cssClass} ${anno.id ? anno.id : ""}`
});
elText.attr({
rel: index
});
parent.appendChild(elText.node);
if (anno.customSVG.SVG) {
const g = this.annoCtx.graphics.group({
class: "apexcharts-point-annotations-custom-svg " + anno.customSVG.cssClass
});
g.attr({
transform: `translate(${x + anno.customSVG.offsetX}, ${y + anno.customSVG.offsetY})`
});
g.node.innerHTML = anno.customSVG.SVG;
parent.appendChild(g.node);
}
if (anno.image.path) {
const imgWidth = anno.image.width ? anno.image.width : 20;
const imgHeight = anno.image.height ? anno.image.height : 20;
point = this.annoCtx.addImage({
x: x + anno.image.offsetX - imgWidth / 2,
y: y + anno.image.offsetY - imgHeight / 2,
width: imgWidth,
height: imgHeight,
path: anno.image.path,
appendTo: ".apexcharts-point-annotations"
});
}
if (anno.mouseEnter) {
point.node.addEventListener(
"mouseenter",
anno.mouseEnter.bind(this, anno)
);
}
if (anno.mouseLeave) {
point.node.addEventListener(
"mouseleave",
anno.mouseLeave.bind(this, anno)
);
}
if (anno.click) {
point.node.addEventListener("click", anno.click.bind(this, anno));
}
}
}
drawPointAnnotations() {
const w = this.w;
const elg = this.annoCtx.graphics.group({
class: "apexcharts-point-annotations"
});
w.config.annotations.points.map(
(anno, index) => {
this.addPointAnnotation(anno, elg.node, index);
}
);
return elg;
}
}
class Annotations {
/**
* @param {import('../../types/internal').ChartStateW} w
*/
constructor(w, { theme = null, timeScale = null } = {}) {
this.w = w;
this.theme = theme;
this.timeScale = timeScale;
this.invertAxis = void 0;
this.inversedReversedAxis = void 0;
this.graphics = new Graphics(this.w);
if (this.w.globals.isBarHorizontal) {
this.invertAxis = true;
}
this.helpers = new Helpers$2(this);
this.xAxisAnnotations = new XAnnotations(this);
this.yAxisAnnotations = new YAnnotations(this);
this.pointsAnnotations = new PointAnnotations(this);
if (this.w.globals.isBarHorizontal && this.w.config.yaxis[0].reversed) {
this.inversedReversedAxis = true;
}
this.xDivision = this.w.layout.gridWidth / this.w.globals.dataPoints;
}
drawAxesAnnotations() {
const w = this.w;
if (w.globals.axisCharts && w.globals.dataPoints) {
const yAnnotations = this.yAxisAnnotations.drawYAxisAnnotations();
const xAnnotations = this.xAxisAnnotations.drawXAxisAnnotations();
const pointAnnotations = this.pointsAnnotations.drawPointAnnotations();
const initialAnim = w.config.chart.animations.enabled;
const annoArray = [yAnnotations, xAnnotations, pointAnnotations];
const annoElArray = [
xAnnotations.node,
yAnnotations.node,
pointAnnotations.node
];
for (let i = 0; i < 3; i++) {
w.dom.elGraphical.add(annoArray[i]);
if (initialAnim && !w.globals.resized && !w.globals.dataChanged) {
if (w.config.chart.type !== "scatter" && w.config.chart.type !== "bubble" && w.globals.dataPoints > 1) {
annoElArray[i].classList.add("apexcharts-element-hidden");
}
}
w.globals.delayedElements.push({ el: annoElArray[i], index: 0 });
}
this.helpers.annotationsBackground();
}
}
drawImageAnnos() {
const w = this.w;
w.config.annotations.images.map((s) => {
this.addImage(s);
});
}
drawTextAnnos() {
const w = this.w;
w.config.annotations.texts.map((t) => {
this.addText(t);
});
}
/**
* @param {Record<string, any>} anno
* @param {Element} parent
* @param {number} index
*/
addXaxisAnnotation(anno, parent, index) {
this.xAxisAnnotations.addXaxisAnnotation(anno, parent, index);
}
/**
* @param {Record<string, any>} anno
* @param {Element} parent
* @param {number} index
*/
addYaxisAnnotation(anno, parent, index) {
this.yAxisAnnotations.addYaxisAnnotation(anno, parent, index);
}
/**
* @param {Record<string, any>} anno
* @param {Element} parent
* @param {number} index
*/
addPointAnnotation(anno, parent, index) {
this.pointsAnnotations.addPointAnnotation(anno, parent, index);
}
/**
* @param {Record<string, any>} params
*/
addText(params) {
const {
x,
y,
text,
textAnchor,
foreColor,
fontSize,
fontFamily,
fontWeight,
cssClass,
backgroundColor,
borderWidth,
strokeDashArray,
borderRadius,
borderColor,
appendTo = ".apexcharts-svg",
paddingLeft = 4,
paddingRight = 4,
paddingBottom = 2,
paddingTop = 2
} = params;
const w = this.w;
const elText = this.graphics.drawText({
x,
y,
text,
textAnchor: textAnchor || "start",
fontSize: fontSize || "12px",
fontWeight: fontWeight || "regular",
fontFamily: fontFamily || w.config.chart.fontFamily,
foreColor: foreColor || w.config.chart.foreColor,
cssClass: "apexcharts-text " + cssClass ? cssClass : ""
});
const parent = w.dom.baseEl.querySelector(appendTo);
if (parent) {
parent.appendChild(elText.node);
}
const textRect = elText.bbox();
if (text) {
const elRect = this.graphics.drawRect(
textRect.x - paddingLeft,
textRect.y - paddingTop,
textRect.width + paddingLeft + paddingRight,
textRect.height + paddingBottom + paddingTop,
borderRadius,
backgroundColor ? backgroundColor : "transparent",
1,
borderWidth,
borderColor,
strokeDashArray
);
parent.insertBefore(elRect.node, elText.node);
}
}
/**
* @param {Record<string, any>} params
*/
addImage(params) {
const w = this.w;
const {
path,
x = 0,
y = 0,
width = 20,
height = 20,
appendTo = ".apexcharts-svg"
} = params;
const img = w.dom.Paper.image(path);
img.size(width, height).move(x, y);
const parent = w.dom.baseEl.querySelector(appendTo);
if (parent) {
parent.appendChild(img.node);
}
return img;
}
// The addXaxisAnnotation method requires a parent class, and user calling this method externally on the chart instance may not specify parent, hence a different method
/**
* @param {Record<string, any>} params
* @param {boolean} pushToMemory
* @param {any} context
*/
addXaxisAnnotationExternal(params, pushToMemory, context) {
this.addAnnotationExternal({
params,
pushToMemory,
context,
type: "xaxis",
contextMethod: context.addXaxisAnnotation
});
return context;
}
/**
* @param {Record<string, any>} params
* @param {boolean} pushToMemory
* @param {any} context
*/
addYaxisAnnotationExternal(params, pushToMemory, context) {
this.addAnnotationExternal({
params,
pushToMemory,
context,
type: "yaxis",
contextMethod: context.addYaxisAnnotation
});
return context;
}
/**
* @param {Record<string, any>} params
* @param {boolean} pushToMemory
* @param {any} context
*/
addPointAnnotationExternal(params, pushToMemory, context) {
if (typeof this.invertAxis === "undefined") {
this.invertAxis = context.w.globals.isBarHorizontal;
}
this.addAnnotationExternal({
params,
pushToMemory,
context,
type: "point",
contextMethod: context.addPointAnnotation
});
return context;
}
/** @param {{params: any, pushToMemory: any, context: any, type: any, contextMethod: any}} opts */
addAnnotationExternal({
params,
pushToMemory,
context,
type,
contextMethod
}) {
const me = context;
const w = me.w;
const parent = w.dom.baseEl.querySelector(`.apexcharts-${type}-annotations`);
const index = parent.childNodes.length + 1;
const options2 = new Options();
const axesAnno = Object.assign(
{},
type === "xaxis" ? options2.xAxisAnnotation : type === "yaxis" ? options2.yAxisAnnotation : options2.pointAnnotation
);
const anno = Utils$1.extend(axesAnno, params);
switch (type) {
case "xaxis":
this.addXaxisAnnotation(anno, parent, index);
break;
case "yaxis":
this.addYaxisAnnotation(anno, parent, index);
break;
case "point":
this.addPointAnnotation(anno, parent, index);
break;
}
const axesAnnoLabel = w.dom.baseEl.querySelector(
`.apexcharts-${type}-annotations .apexcharts-${type}-annotation-label[rel='${index}']`
);
const elRect = this.helpers.addBackgroundToAnno(axesAnnoLabel, anno);
if (elRect) {
parent.insertBefore(elRect.node, axesAnnoLabel);
}
if (pushToMemory) {
w.globals.memory.methodsToExec.push({
context: me,
id: anno.id ? anno.id : Utils$1.randomId(),
method: contextMethod,
label: "addAnnotation",
params
});
}
return context;
}
/**
* @param {import('../../types/internal').ChartContext} ctx
*/
clearAnnotations(ctx) {
const w = ctx.w;
const annos = w.dom.baseEl.querySelectorAll(
".apexcharts-yaxis-annotations, .apexcharts-xaxis-annotations, .apexcharts-point-annotations"
);
for (let i = w.globals.memory.methodsToExec.length - 1; i >= 0; i--) {
if (w.globals.memory.methodsToExec[i].label === "addText" || w.globals.memory.methodsToExec[i].label === "addAnnotation") {
w.globals.memory.methodsToExec.splice(i, 1);
}
}
Array.prototype.forEach.call(annos, (a) => {
while (a.firstChild) {
a.removeChild(a.firstChild);
}
});
}
/**
* @param {import('../../types/internal').ChartContext} ctx
* @param {string} id
*/
removeAnnotation(ctx, id) {
const w = ctx.w;
const annos = w.dom.baseEl.querySelectorAll(`.${id}`);
if (annos) {
w.globals.memory.methodsToExec.map((m, i) => {
if (m.id === id) {
w.globals.memory.methodsToExec.splice(i, 1);
}
});
Object.keys(w.config.annotations).forEach((key) => {
const annotationArray = w.config.annotations[key];
if (Array.isArray(annotationArray)) {
w.config.annotations[key] = annotationArray.filter((m) => m.id !== id);
}
});
Array.prototype.forEach.call(annos, (a) => {
a.parentElement.removeChild(a);
});
}
}
}
ApexCharts.registerFeatures({ annotations: Annotations });
class KeyboardNavigation {
/**
* @param {import('../../types/internal').ChartStateW} w
* @param {import('../../types/internal').ChartContext} ctx
*/
constructor(w, ctx) {
this.w = w;
this.ctx = ctx;
this.seriesIndex = 0;
this.dataPointIndex = 0;
this.active = false;
this._tooltipDismissed = false;
this._focusedEl = null;
this._hoveredBarEl = null;
this._enlargedScatterMarker = null;
this._onKeyDown = this._onKeyDown.bind(this);
this._onFocus = this._onFocus.bind(this);
this._onBlur = this._onBlur.bind(this);
this._onLegendClick = this._onLegendClick.bind(this);
}
// ─── Public API ───────────────────────────────────────────────────────────
/**
* Called after the chart and tooltip have been fully rendered.
* Attaches event listeners and makes the SVG keyboard-focusable.
*/
init() {
const w = this.w;
const svgEl = w.dom.Paper.node;
if (!svgEl) return;
svgEl.setAttribute("tabindex", "0");
svgEl.addEventListener("focus", this._onFocus);
svgEl.addEventListener("blur", this._onBlur);
svgEl.addEventListener("keydown", this._onKeyDown, { passive: false });
this.ctx.events.addEventListener("legendClick", this._onLegendClick);
}
/**
* Removes all event listeners. Called from chart.destroy().
*/
destroy() {
const w = this.w;
const svgEl = w.dom.Paper && w.dom.Paper.node;
if (!svgEl) return;
svgEl.removeEventListener("focus", this._onFocus);
svgEl.removeEventListener("blur", this._onBlur);
svgEl.removeEventListener("keydown", this._onKeyDown);
this.ctx.events.removeEventListener("legendClick", this._onLegendClick);
}
/**
* Called from Events.js keydown handler. Navigation keys are already handled
* by the direct SVG listener (which can call preventDefault). This entry
* point is intentionally a no-op — Events.js still fires the public keyDown
* callback and fireEvent('keydown') independently.
* @param {Event} _e
*/
handleKey(_e) {
}
// ─── Focus / blur ─────────────────────────────────────────────────────────
_onFocus() {
if (!this._isNavEnabled()) return;
this.active = true;
this._clampCursor();
this._snapToVisibleRange();
this._showCurrentPoint();
}
_onBlur() {
this.active = false;
this._tooltipDismissed = false;
this._hideFocus();
}
// Called when the user clicks a legend item (collapse/expand a series).
// Hide the keyboard-nav tooltip — the chart is about to re-render and the
// current position may no longer be valid.
_onLegendClick() {
if (!this.active) return;
this.active = false;
this._hideFocus();
}
// ─── Key handler ──────────────────────────────────────────────────────────
/**
* @param {KeyboardEvent} e
*/
_onKeyDown(e) {
var _a, _b, _c;
if (!this._isNavEnabled() || !this.active) return;
if (e.shiftKey && (e.key === "ArrowRight" || e.key === "ArrowLeft") && this._canPan()) {
e.preventDefault();
this._panBy(e.key === "ArrowRight" ? 1 : -1);
return;
}
switch (e.key) {
case "ArrowRight":
e.preventDefault();
this._move(0, 1);
break;
case "ArrowLeft":
e.preventDefault();
this._move(0, -1);
break;
case "ArrowUp":
e.preventDefault();
this._move(-1, 0);
break;
case "ArrowDown":
e.preventDefault();
this._move(1, 0);
break;
case "Home":
e.preventDefault();
this.dataPointIndex = 0;
this._skipNullForward();
this._showCurrentPoint();
break;
case "End":
e.preventDefault();
this.dataPointIndex = this._getDataPointCount(this.seriesIndex) - 1;
this._skipNullBackward();
this._showCurrentPoint();
break;
case "Enter":
case " ":
e.preventDefault();
this._fireClick();
break;
case "+":
case "=":
if (this._canZoom()) {
e.preventDefault();
(_a = this.ctx.toolbar) == null ? void 0 : _a.handleZoomIn();
this._announce("Zoomed in");
}
break;
case "-":
case "_":
if (this._canZoom()) {
e.preventDefault();
(_b = this.ctx.toolbar) == null ? void 0 : _b.handleZoomOut();
this._announce("Zoomed out");
}
break;
case "0":
if (this._canZoom() && this.w.interact.zoomed) {
e.preventDefault();
(_c = this.ctx.toolbar) == null ? void 0 : _c.handleZoomReset();
this._announce("Zoom reset");
}
break;
case "Escape":
e.preventDefault();
if (!this._tooltipDismissed) {
this._tooltipDismissed = true;
this._hideFocus();
} else {
this.active = false;
this._tooltipDismissed = false;
this._hideFocus();
}
break;
}
}
// ─── Zoom / pan (keyboard alternatives for drag gestures) ─────────────────
_canZoom() {
const w = this.w;
return Boolean(
w.globals.axisCharts && w.config.chart.zoom && w.config.chart.zoom.enabled
);
}
_canPan() {
return this._canZoom();
}
/**
* Shift the visible x-range by ~10% in the given direction.
* @param {number} direction +1 = right, -1 = left
*/
_panBy(direction) {
const w = this.w;
const toolbar = this.ctx.toolbar;
if (!toolbar) return;
const minX = Number(w.globals.minX);
const maxX = Number(w.globals.maxX);
if (!isFinite(minX) || !isFinite(maxX) || minX === maxX) return;
const span = maxX - minX;
const step = span * 0.1 * direction;
toolbar.zoomUpdateOptions(minX + step, maxX + step);
this._announce(direction > 0 ? "Panned right" : "Panned left");
}
// ─── Navigation ───────────────────────────────────────────────────────────
/**
* @param {number} dSeries
* @param {number} dPoint
*/
_move(dSeries, dPoint) {
const w = this.w;
const wrapAround = w.config.chart.accessibility.keyboard.navigation.wrapAround;
if (dSeries !== 0) {
const ttCtx = this.w.globals.tooltip;
if (ttCtx && ttCtx.tConfig && ttCtx.tConfig.shared) {
const j = this.dataPointIndex;
const isActuallyShared = ttCtx.tooltipUtil && ttCtx.tooltipUtil.isXoverlap(j) && ttCtx.tooltipUtil.isInitialSeriesSameLen();
if (isActuallyShared) return;
}
const total = this._getSeriesCount();
let si = this.seriesIndex + dSeries;
let attempts = 0;
while (attempts < total) {
if (si < 0) si = wrapAround ? total - 1 : 0;
if (si >= total) si = wrapAround ? 0 : total - 1;
if (!w.globals.collapsedSeriesIndices.includes(si)) break;
si += dSeries;
attempts++;
}
this.seriesIndex = si;
const dpCount = this._getDataPointCount(si);
if (this.dataPointIndex >= dpCount) {
this.dataPointIndex = dpCount - 1;
}
}
if (dPoint !== 0) {
const dpCount = this._getDataPointCount(this.seriesIndex);
let di = this.dataPointIndex + dPoint;
if (di < 0) di = wrapAround ? dpCount - 1 : 0;
if (di >= dpCount) di = wrapAround ? 0 : dpCount - 1;
this.dataPointIndex = di;
if (dPoint > 0) {
this._skipNullForward();
} else {
this._skipNullBackward();
}
if (!this._isDataPointVisible(this.seriesIndex, this.dataPointIndex)) {
this._snapToVisibleRangeInDirection(dPoint);
}
}
this._showCurrentPoint();
}
/** Advance dataPointIndex forward past any nulls */
_skipNullForward() {
const w = this.w;
const si = this.seriesIndex;
const dpCount = this._getDataPointCount(si);
let di = this.dataPointIndex;
let attempts = 0;
if (!Array.isArray(w.seriesData.series[si])) return;
while (attempts < dpCount && w.seriesData.series[si][di] === null) {
di = (di + 1) % dpCount;
attempts++;
}
this.dataPointIndex = di;
}
/** Retreat dataPointIndex backward past any nulls */
_skipNullBackward() {
const w = this.w;
const si = this.seriesIndex;
const dpCount = this._getDataPointCount(si);
let di = this.dataPointIndex;
let attempts = 0;
if (!Array.isArray(w.seriesData.series[si])) return;
while (attempts < dpCount && w.seriesData.series[si][di] === null) {
di = (di - 1 + dpCount) % dpCount;
attempts++;
}
this.dataPointIndex = di;
}
// ─── Display ──────────────────────────────────────────────────────────────
_showCurrentPoint() {
const { seriesIndex: i, dataPointIndex: j } = this;
const w = this.w;
const ttCtx = w.globals.tooltip;
if (!ttCtx || !ttCtx.ttItems) return;
w.interact.capturedSeriesIndex = i;
w.interact.capturedDataPointIndex = j;
this._applyFocusClass(i, j);
this._showTooltip(
i,
j,
/** @type {any} */
ttCtx
);
}
_hideFocus() {
const w = this.w;
const ttCtx = (
/** @type {any} */
w.globals.tooltip
);
this._removeFocusClass();
this._leaveHoveredBar();
if (!ttCtx) return;
if (ttCtx.marker) {
ttCtx.marker.resetPointsSize();
}
this._enlargedScatterMarker = null;
const tooltipEl = ttCtx.getElTooltip();
if (tooltipEl) {
tooltipEl.classList.remove("apexcharts-active");
if (w.config.chart.accessibility.enabled && w.config.chart.accessibility.announcements.enabled) {
tooltipEl.setAttribute("aria-hidden", "true");
}
}
w.dom.baseEl.classList.remove("apexcharts-tooltip-active");
const xcrosshairs = ttCtx.getElXCrosshairs();
if (xcrosshairs) xcrosshairs.classList.remove("apexcharts-active");
}
// ─── Tooltip display per chart type ───────────────────────────────────────
/**
* @param {number} i
* @param {number} j
* @param {import('../tooltip/Tooltip').default} ttCtx
*/
_showTooltip(i, j, ttCtx) {
const w = this.w;
const type = w.config.chart.type;
const tooltipEl = ttCtx.getElTooltip();
if (!tooltipEl) return;
const cachedDims = ttCtx.getCachedDimensions();
ttCtx.tooltipRect = {
x: 0,
y: 0,
ttWidth: cachedDims.ttWidth || 0,
ttHeight: cachedDims.ttHeight || 0
};
this._setSyntheticEvent(i, j, ttCtx);
w.dom.baseEl.classList.add("apexcharts-tooltip-active");
tooltipEl.classList.add("apexcharts-active");
if (w.config.chart.accessibility.enabled && w.config.chart.accessibility.announcements.enabled) {
tooltipEl.removeAttribute("aria-hidden");
}
if (type === "pie" || type === "donut" || type === "polarArea") {
this._showTooltipNonAxis(i, j, ttCtx, tooltipEl);
} else if (type === "radialBar") {
this._showTooltipRadialBar(i, j, ttCtx, tooltipEl);
} else if (type === "heatmap" || type === "treemap") {
this._showTooltipHeatTree(i, j, ttCtx, tooltipEl, type);
} else if (type === "bar" || type === "candlestick" || type === "boxPlot" || type === "rangeBar") {
this._showTooltipBar(i, j, ttCtx);
} else {
this._showTooltipAxisLine(i, j, ttCtx);
}
}
/**
* Set ttCtx.e to a synthetic mouse-event-like object whose clientX/Y point
* to the centre of the current data-point element. This ensures that any
* positioning helper that reads ttCtx.e (followCursor path in moveTooltip,
* moveStickyTooltipOverBars, moveDynamicPointsOnHover, etc.) gets valid
* coordinates rather than crashing on undefined.
*
* For chart types that don't have a concrete SVG element per data point
* (pie, radialBar) we fall back to the SVG centre.
* @param {number} i
* @param {number} j
* @param {import('../tooltip/Tooltip').default} ttCtx
*/
_setSyntheticEvent(i, j, ttCtx) {
const w = this.w;
const type = w.config.chart.type;
let clientX = 0;
let clientY = 0;
const el = this._getFocusableElement(i, j);
if (el) {
const rect = el.getBoundingClientRect();
clientX = rect.left + rect.width / 2;
clientY = rect.top + rect.height / 2;
} else if (w.globals.pointsArray && w.globals.pointsArray[i] && w.globals.pointsArray[i][j]) {
const pt = w.globals.pointsArray[i][j];
const elGrid = ttCtx.getElGrid && ttCtx.getElGrid();
if (elGrid) {
const gridRect = elGrid.getBoundingClientRect();
clientX = gridRect.left + (pt[0] || 0);
clientY = gridRect.top + (pt[1] || 0);
}
} else {
const svgEl = w.dom.Paper && w.dom.Paper.node;
if (svgEl) {
const svgRect = svgEl.getBoundingClientRect();
clientX = svgRect.left + svgRect.width / 2;
clientY = svgRect.top + svgRect.height / 2;
}
}
if (type === "line" || type === "area" || type === "rangeArea" || type === "scatter" || type === "bubble" || type === "radar") {
if (w.globals.pointsArray && w.globals.pointsArray[i] && w.globals.pointsArray[i][j]) {
const pt = w.globals.pointsArray[i][j];
const elGrid = ttCtx.getElGrid && ttCtx.getElGrid();
if (elGrid) {
const gridRect = elGrid.getBoundingClientRect();
clientX = gridRect.left + (pt[0] || 0);
clientY = gridRect.top + (pt[1] || 0);
}
}
}
ttCtx.e = { type: "mousemove", clientX, clientY };
}
/**
* bar / column / candlestick / boxPlot / rangeBar
* @param {number} i
* @param {number} j
* @param {import('../tooltip/Tooltip').default} ttCtx
*/
_showTooltipBar(i, j, ttCtx) {
var _a, _b, _c, _d;
const w = this.w;
const shared = ttCtx.tConfig.shared && (ttCtx.tooltipUtil.isXoverlap(j) || w.globals.isBarHorizontal) && ttCtx.tooltipUtil.isInitialSeriesSameLen();
const rangeData = (
/** @type {any} */
(_d = (_c = (_b = (_a = w.rangeData.seriesRange) == null ? void 0 : _a[i]) == null ? void 0 : _b[j]) == null ? void 0 : _c.y) == null ? void 0 : _d[0]
);
ttCtx.tooltipLabels.drawSeriesTexts(__spreadProps(__spreadValues(__spreadValues({
ttItems: ttCtx.ttItems,
i,
j
}, (rangeData == null ? void 0 : rangeData.y1) !== void 0 && { y1: rangeData.y1 }), (rangeData == null ? void 0 : rangeData.y2) !== void 0 && { y2: rangeData.y2 }), {
shared
}));
const parent = `.apexcharts-series[data\\:realIndex='${i}']`;
const elPath = w.dom.Paper.findOne(
`${parent} path[j='${j}'], ${parent} circle[j='${j}'], ${parent} rect[j='${j}']`
);
if (elPath) {
this._leaveHoveredBar();
const graphics = new Graphics(this.w, this.ctx);
graphics.pathMouseEnter(elPath, null);
this._hoveredBarEl = elPath;
}
if (w.globals.isBarHorizontal) {
const barDomEl = elPath && elPath.node;
if (barDomEl) {
const wrapRect = w.dom.elWrap.getBoundingClientRect();
const barRect = barDomEl.getBoundingClientRect();
const barCx = barRect.left - wrapRect.left;
const barCy = barRect.top - wrapRect.top;
const bh = barRect.height;
const bw = barRect.width;
const ttWidth = ttCtx.tooltipRect.ttWidth || 0;
const ttHeight = ttCtx.tooltipRect.ttHeight || 0;
const y = barCy + bh / 2 - ttHeight / 2;
let x = barCx + bw;
const baselineX = ttCtx.xyRatios && ttCtx.xyRatios.baseLineInvertedY != null ? ttCtx.xyRatios.baseLineInvertedY : wrapRect.width / 2;
if (barCx < baselineX) {
x = barCx - ttWidth;
}
const tooltipEl = ttCtx.getElTooltip();
if (tooltipEl) {
tooltipEl.style.left = x + "px";
tooltipEl.style.top = y + "px";
}
}
} else {
ttCtx.tooltipPosition.moveStickyTooltipOverBars(j, i);
}
}
/**
* line / area / scatter / bubble / radar / rangeArea
* @param {number} i
* @param {number} j
* @param {import('../tooltip/Tooltip').default} ttCtx
*/
_showTooltipAxisLine(i, j, ttCtx) {
const w = this.w;
const type = w.config.chart.type;
const sharedConfigured = ttCtx.tConfig.shared;
const shared = sharedConfigured && ttCtx.tooltipUtil.isXoverlap(j) && ttCtx.tooltipUtil.isInitialSeriesSameLen();
ttCtx.tooltipLabels.drawSeriesTexts({
ttItems: ttCtx.ttItems,
i,
j,
shared
});
const isScatterLike = type === "scatter" || type === "bubble";
const hasVisibleMarkers = w.globals.markers.largestSize > 0;
if (isScatterLike) {
this._showScatterBubblePoint(i, j, ttCtx);
} else if (hasVisibleMarkers) {
if (shared) {
ttCtx.marker.enlargePoints(j);
} else {
ttCtx.tooltipPosition.moveDynamicPointOnHover(j, i);
}
} else if (shared) {
ttCtx.tooltipPosition.moveDynamicPointsOnHover(j);
} else {
ttCtx.tooltipPosition.moveDynamicPointOnHover(j, i);
}
}
/**
* Scatter / bubble: find the specific marker element for (seriesIndex i,
* dataPointIndex j), resize only that element, and position the tooltip at
* its coordinates — mirroring what Position.moveMarkers does for mouse hover.
*
* Unlike enlargePoints(j) which queries ALL series for rel===j (causing
* multiple bubbles to enlarge and tooltip to land on the wrong one), this
* method queries by both series index AND data-point index for precision.
* @param {number} i
* @param {number} j
* @param {import('../tooltip/Tooltip').default} ttCtx
*/
_showScatterBubblePoint(i, j, ttCtx) {
const baseEl = this.w.dom.baseEl;
if (this._enlargedScatterMarker) {
ttCtx.marker.oldPointSize(this._enlargedScatterMarker);
this._enlargedScatterMarker = null;
}
const seriesEl = baseEl.querySelector(
`.apexcharts-series[data\\:realIndex='${i}']`
);
if (!seriesEl) return;
const markerEl = seriesEl.querySelector(`.apexcharts-marker[rel='${j}']`);
if (!markerEl) return;
ttCtx.marker.enlargeCurrentPoint(j, markerEl);
this._enlargedScatterMarker = markerEl;
}
/**
* pie / donut / polarArea
* @param {number} i
* @param {number} j
* @param {import('../tooltip/Tooltip').default} ttCtx
* @param {HTMLElement} tooltipEl
*/
_showTooltipNonAxis(i, j, ttCtx, tooltipEl) {
var _a, _b;
const w = this.w;
ttCtx.tooltipLabels.drawSeriesTexts({
ttItems: ttCtx.ttItems,
i: j,
shared: false
});
const tooltipBound = tooltipEl.getBoundingClientRect();
const ttWidth = tooltipBound.width || ttCtx.tooltipRect.ttWidth || 0;
const ttHeight = tooltipBound.height || ttCtx.tooltipRect.ttHeight || 0;
const sliceEl = w.dom.baseEl.querySelector(`.apexcharts-pie-area[j='${j}']`);
if (sliceEl) {
const cx = parseFloat((_a = sliceEl.getAttribute("data:cx")) != null ? _a : "");
const cy = parseFloat((_b = sliceEl.getAttribute("data:cy")) != null ? _b : "");
if (!isNaN(cx) && !isNaN(cy)) {
const svgBound = w.dom.Paper.node.getBoundingClientRect();
const wrapBound = w.dom.elWrap.getBoundingClientRect();
const offsetX = svgBound.left - wrapBound.left;
const offsetY = svgBound.top - wrapBound.top;
tooltipEl.style.left = offsetX + cx - ttWidth / 2 + "px";
tooltipEl.style.top = offsetY + cy - ttHeight - 10 + "px";
}
}
}
/**
* radialBar — one ring per series, single value each
* @param {number} i
* @param {any} _j
* @param {import('../tooltip/Tooltip').default} ttCtx
* @param {HTMLElement} tooltipEl
*/
_showTooltipRadialBar(i, _j, ttCtx, tooltipEl) {
var _a;
const w = this.w;
ttCtx.tooltipLabels.drawSeriesTexts({
ttItems: ttCtx.ttItems,
i,
shared: false
});
const { ttWidth = 0, ttHeight = 0 } = ttCtx.getCachedDimensions();
const arcEl = w.dom.baseEl.querySelector(
`.apexcharts-radialbar-series[data\\:realIndex='${i}'] path`
);
if (arcEl) {
const angle = parseFloat((_a = arcEl.getAttribute("data:angle")) != null ? _a : "") || 0;
const initialAngle = w.config.plotOptions.radialBar.startAngle || 0;
const midAngle = initialAngle + angle / 2;
const centerX = w.layout.gridWidth / 2;
const centerY = w.layout.gridHeight / 2;
const radialSize = w.globals.radialSize || Math.min(w.layout.gridWidth, w.layout.gridHeight) / 2;
const seriesCount = w.seriesData.series.length;
const trackSize = radialSize / Math.max(seriesCount, 1);
const outerRadius = radialSize - i * trackSize;
const innerRadius = outerRadius - trackSize;
const ringRadius = (outerRadius + innerRadius) / 2;
const centroid = Utils$1.polarToCartesian(
centerX,
centerY,
ringRadius,
midAngle
);
const x = centroid.x + (w.layout.translateX || 0);
const y = centroid.y + (w.layout.translateY || 0);
tooltipEl.style.left = x - ttWidth / 2 + "px";
tooltipEl.style.top = y - ttHeight - 10 + "px";
}
}
/**
* heatmap / treemap — position tooltip using element bounding rect
* @param {number} i
* @param {number} j
* @param {import('../tooltip/Tooltip').default} ttCtx
* @param {HTMLElement} tooltipEl
* @param {string} type
*/
_showTooltipHeatTree(i, j, ttCtx, tooltipEl, type) {
var _a, _b;
const w = this.w;
ttCtx.tooltipLabels.drawSeriesTexts({
ttItems: ttCtx.ttItems,
i,
j,
shared: false
});
const tooltipRect = tooltipEl.getBoundingClientRect();
const ttWidth = tooltipRect.width || ttCtx.tooltipRect.ttWidth || 0;
const ttHeight = tooltipRect.height || ttCtx.tooltipRect.ttHeight || 0;
const rectClass = type === "heatmap" ? "apexcharts-heatmap-rect" : "apexcharts-treemap-rect";
const cell = w.dom.baseEl.querySelector(`.${rectClass}[i='${i}'][j='${j}']`);
if (cell) {
const wrapRect = w.dom.elWrap.getBoundingClientRect();
const cellRect = cell.getBoundingClientRect();
const cellCx = cellRect.left - wrapRect.left;
const cellCy = cellRect.top - wrapRect.top;
const cellWidth = cellRect.width;
const cellHeight = cellRect.height;
const cx = parseFloat((_a = cell.getAttribute("cx")) != null ? _a : "");
const cellWidthAttr = parseFloat((_b = cell.getAttribute("width")) != null ? _b : "");
ttCtx.tooltipPosition.moveXCrosshairs(cx + cellWidthAttr / 2);
let x = cellCx + cellWidth + ttWidth / 2;
const y = cellCy + cellHeight / 2 - ttHeight / 2;
if (cellCx + cellWidth > w.layout.gridWidth / 2) {
x = cellCx - ttWidth / 2;
}
tooltipEl.style.left = x + "px";
tooltipEl.style.top = y + "px";
}
}
// ─── Focus class management ───────────────────────────────────────────────
/**
* @param {number} i
* @param {number} j
*/
_applyFocusClass(i, j) {
this._removeFocusClass();
const el = this._getFocusableElement(i, j);
if (el) {
el.classList.add("apexcharts-keyboard-focused");
el.setAttribute("role", "img");
const label = this._buildPointLabel(i, j);
if (label) el.setAttribute("aria-label", label);
this._focusedEl = el;
}
}
_removeFocusClass() {
if (this._focusedEl) {
this._focusedEl.classList.remove("apexcharts-keyboard-focused");
this._focusedEl.removeAttribute("role");
this._focusedEl.removeAttribute("aria-label");
this._focusedEl = null;
}
}
/**
* Build an accessible label for the data point at (i, j) using the same
* formatters the visible tooltip / axis labels use, so SR output matches
* the visual presentation.
* @param {number} i
* @param {number} j
* @returns {string}
*/
_buildPointLabel(i, j) {
var _a, _b, _c, _d, _e, _f, _g, _h;
const w = this.w;
const type = w.config.chart.type;
const seriesNames = w.seriesData.seriesNames || [];
const series = w.seriesData.series || [];
if (type === "pie" || type === "donut" || type === "polarArea") {
const sliceLabel = (_b = ((_a = w.labelData) == null ? void 0 : _a.labels) && w.labelData.labels[j]) != null ? _b : "";
const value = Array.isArray(series) ? series[j] : "";
return sliceLabel ? `${sliceLabel}: ${value}` : `${value}`;
}
if (type === "radialBar") {
const seriesName2 = seriesNames[i] || `Series ${i + 1}`;
const value = Array.isArray(series) ? series[i] : "";
return `${seriesName2}: ${value}`;
}
const seriesName = seriesNames[i] || `Series ${i + 1}`;
const row = Array.isArray(series[i]) ? series[i] : [];
const rawValue = row[j];
let formattedValue = rawValue == null ? "" : String(rawValue);
const yFormatter = (_d = (_c = w.formatters) == null ? void 0 : _c.yLabelFormatters) == null ? void 0 : _d[i];
if (typeof yFormatter === "function") {
try {
formattedValue = yFormatter(rawValue, {
seriesIndex: i,
dataPointIndex: j,
w
});
} catch (e) {
}
}
let category = "";
const categoryLabels = (_e = w.labelData) == null ? void 0 : _e.categoryLabels;
const seriesX = (_g = (_f = w.seriesData) == null ? void 0 : _f.seriesX) == null ? void 0 : _g[i];
if (Array.isArray(categoryLabels) && categoryLabels[j] != null) {
category = String(categoryLabels[j]);
} else if (Array.isArray(seriesX) && seriesX[j] != null) {
const xFormatter = (_h = w.formatters) == null ? void 0 : _h.xLabelFormatter;
if (typeof xFormatter === "function") {
try {
category = String(
xFormatter(seriesX[j], { seriesIndex: i, dataPointIndex: j, w })
);
} catch (e) {
category = String(seriesX[j]);
}
} else {
category = String(seriesX[j]);
}
}
return category ? `${seriesName}: ${formattedValue}, ${category}` : `${seriesName}: ${formattedValue}`;
}
_leaveHoveredBar() {
if (this._hoveredBarEl) {
const graphics = new Graphics(this.w, this.ctx);
graphics.pathMouseLeave(this._hoveredBarEl, null);
this._hoveredBarEl = null;
}
}
/**
* @param {number} i
* @param {number} j
*/
_getFocusableElement(i, j) {
const w = this.w;
const type = w.config.chart.type;
const baseEl = w.dom.baseEl;
if (type === "pie" || type === "donut" || type === "polarArea") {
return baseEl.querySelector(`.apexcharts-pie-area[j='${j}']`);
}
if (type === "heatmap") {
return baseEl.querySelector(
`.apexcharts-heatmap-rect[i='${i}'][j='${j}']`
);
}
if (type === "treemap") {
return baseEl.querySelector(
`.apexcharts-treemap-rect[i='${i}'][j='${j}']`
);
}
if (type === "radialBar") {
return baseEl.querySelector(
`.apexcharts-radialbar-series[data\\:realIndex='${i}'] path`
);
}
if (type === "bar" || type === "candlestick" || type === "boxPlot" || type === "rangeBar") {
return baseEl.querySelector(
`.apexcharts-series[data\\:realIndex='${i}'] path[j='${j}']`
);
}
const marker = baseEl.querySelector(
`.apexcharts-series[data\\:realIndex='${i}'] .apexcharts-marker[rel='${j}']`
);
return marker || null;
}
// ─── Click / Enter ────────────────────────────────────────────────────────
_fireClick() {
const w = this.w;
const ttCtx = w.globals.tooltip;
if (!ttCtx) return;
const syntheticEvent = {
type: "mouseup",
clientX: 0,
clientY: 0
};
ttCtx.markerClick(syntheticEvent, this.seriesIndex, this.dataPointIndex);
}
// ─── Helpers ──────────────────────────────────────────────────────────────
_isNavEnabled() {
const a11y = this.w.config.chart.accessibility;
return a11y.enabled && a11y.keyboard.enabled && a11y.keyboard.navigation.enabled;
}
_getSeriesCount() {
const w = this.w;
const type = w.config.chart.type;
if (type === "pie" || type === "donut" || type === "polarArea") {
return 1;
}
return w.seriesData.series.length;
}
/**
* @param {number} si
*/
_getDataPointCount(si) {
const w = this.w;
const type = w.config.chart.type;
if (type === "pie" || type === "donut" || type === "polarArea") {
return w.seriesData.series.length;
}
const series = w.seriesData.series;
return series[si] && Array.isArray(series[si]) ? series[si].length : 0;
}
_clampCursor() {
const seriesCount = this._getSeriesCount();
if (this.seriesIndex >= seriesCount) this.seriesIndex = seriesCount - 1;
if (this.seriesIndex < 0) this.seriesIndex = 0;
const dpCount = this._getDataPointCount(this.seriesIndex);
if (this.dataPointIndex >= dpCount) this.dataPointIndex = dpCount - 1;
if (this.dataPointIndex < 0) this.dataPointIndex = 0;
}
/**
* When the chart is zoomed in, the current dataPointIndex may point to a
* data point that is outside the visible viewport. Snap the cursor to the
* first data point whose x-value falls within [minX, maxX].
*
* Only adjusts when w.seriesData.seriesX is populated (numeric/datetime axes).
* Category-only charts (seriesX entries are strings or auto-indices) are
* unaffected — all points are always visible.
*/
_snapToVisibleRange() {
const w = this.w;
const gl = w.globals;
const si = this.seriesIndex;
if (!w.interact.zoomed) return;
const seriesX = w.seriesData.seriesX && w.seriesData.seriesX[si];
if (!seriesX || !seriesX.length) return;
const minX = gl.minX;
const maxX = gl.maxX;
if (minX === void 0 || maxX === void 0) return;
const currentX = seriesX[this.dataPointIndex];
if (currentX >= minX && currentX <= maxX) return;
const dpCount = seriesX.length;
for (let di = 0; di < dpCount; di++) {
if (seriesX[di] >= minX && seriesX[di] <= maxX) {
this.dataPointIndex = di;
return;
}
}
}
/**
* Snap to the nearest visible data point in the given navigation direction.
* direction > 0 → find the first visible point (left boundary of zoomed range)
* direction < 0 → find the last visible point (right boundary of zoomed range)
* @param {number} direction
*/
_snapToVisibleRangeInDirection(direction) {
const w = this.w;
const gl = w.globals;
const si = this.seriesIndex;
const seriesX = w.seriesData.seriesX && w.seriesData.seriesX[si];
if (!seriesX || !seriesX.length) return;
const minX = gl.minX;
const maxX = gl.maxX;
if (minX === void 0 || maxX === void 0) return;
const dpCount = seriesX.length;
if (direction >= 0) {
for (let di = 0; di < dpCount; di++) {
if (seriesX[di] >= minX && seriesX[di] <= maxX) {
this.dataPointIndex = di;
return;
}
}
} else {
for (let di = dpCount - 1; di >= 0; di--) {
if (seriesX[di] >= minX && seriesX[di] <= maxX) {
this.dataPointIndex = di;
return;
}
}
}
}
/**
* Check whether the data point at (si, di) is within the current visible
* x-axis range. Used to skip out-of-viewport points during keyboard nav.
* @param {number} si
* @param {number} di
*/
_isDataPointVisible(si, di) {
const w = this.w;
const gl = w.globals;
if (!w.interact.zoomed) return true;
const seriesX = w.seriesData.seriesX && w.seriesData.seriesX[si];
if (!seriesX) return true;
const x = seriesX[di];
if (x === void 0) return true;
return x >= gl.minX && x <= gl.maxX;
}
/**
* Push a short status message to the visually-hidden aria-live region so
* screen readers announce zoom / pan / reset events that have no inherent
* tooltip update. Silently no-op if the region is missing or announcements
* are disabled.
* @param {string} message
*/
_announce(message) {
const w = this.w;
if (!w.config.chart.accessibility.announcements.enabled) return;
const baseEl = w.dom.baseEl;
if (!baseEl) return;
const region = baseEl.querySelector(".apexcharts-sr-status");
if (!region) return;
region.textContent = "";
setTimeout(() => {
region.textContent = message;
}, 0);
}
}
ApexCharts.registerFeatures({ keyboardNavigation: KeyboardNavigation });
class BarDataLabels {
/**
* @param {import('../../../charts/Bar').default} barCtx
*/
constructor(barCtx) {
this.w = barCtx.w;
this.barCtx = barCtx;
this.totalFormatter = this.w.config.plotOptions.bar.dataLabels.total.formatter;
if (!this.totalFormatter) {
this.totalFormatter = this.w.config.dataLabels.formatter;
}
}
/** handleBarDataLabels is used to calculate the positions for the data-labels
* It also sets the element's data attr for bars and calls drawCalculatedBarDataLabels()
* After calculating, it also calls the function to draw data labels
* @memberof Bar
* @param {Record<string, any>} opts - bar properties used throughout the bar drawing function
* @return {object} dataLabels node-element which you can append later
**/
handleBarDataLabels(opts) {
const {
x,
y,
y1,
y2,
i,
j,
realIndex,
columnGroupIndex,
series,
barHeight,
barWidth,
barXPosition,
barYPosition,
visibleSeries
} = opts;
const w = this.w;
const graphics = new Graphics(this.barCtx.w);
const strokeWidth = Array.isArray(this.barCtx.strokeWidth) ? this.barCtx.strokeWidth[realIndex] : this.barCtx.strokeWidth;
let bcx;
let bcy;
if (w.axisFlags.isXNumeric && !w.globals.isBarHorizontal) {
bcx = x + barWidth * (visibleSeries + 1);
bcy = y + barHeight * (visibleSeries + 1) - strokeWidth;
} else {
bcx = x + barWidth * visibleSeries;
bcy = y + barHeight * visibleSeries;
}
let dataLabels = null;
let totalDataLabels = null;
let dataLabelsX = x;
let dataLabelsY = y;
let dataLabelsPos = (
/** @type {any} */
{}
);
const dataLabelsConfig = w.config.dataLabels;
const barDataLabelsConfig = this.barCtx.barOptions.dataLabels;
const barTotalDataLabelsConfig = this.barCtx.barOptions.dataLabels.total;
if (typeof barYPosition !== "undefined" && this.barCtx.isRangeBar) {
bcy = barYPosition;
dataLabelsY = barYPosition;
}
if (typeof barXPosition !== "undefined" && this.barCtx.isVerticalGroupedRangeBar) {
bcx = barXPosition;
dataLabelsX = barXPosition;
}
const offX = dataLabelsConfig.offsetX;
const offY = dataLabelsConfig.offsetY;
let textRects = {
width: 0,
height: 0
};
if (w.config.dataLabels.enabled) {
const yLabel = w.seriesData.series[i][j];
textRects = graphics.getTextRects(
w.config.dataLabels.formatter ? w.config.dataLabels.formatter(yLabel, __spreadProps(__spreadValues({}, w), {
seriesIndex: i,
dataPointIndex: j,
w
})) : w.formatters.yLabelFormatters[0](yLabel),
parseFloat(dataLabelsConfig.style.fontSize).toString()
);
}
const params = {
x,
y,
i,
j,
realIndex,
columnGroupIndex,
bcx,
bcy,
barHeight,
barWidth,
textRects,
strokeWidth,
dataLabelsX,
dataLabelsY,
dataLabelsConfig,
barDataLabelsConfig,
barTotalDataLabelsConfig,
offX,
offY
};
if (this.barCtx.isHorizontal) {
dataLabelsPos = this.calculateBarsDataLabelsPosition(params);
} else {
dataLabelsPos = this.calculateColumnsDataLabelsPosition(params);
}
dataLabels = this.drawCalculatedDataLabels({
x: dataLabelsPos.dataLabelsX,
y: dataLabelsPos.dataLabelsY,
val: this.barCtx.isRangeBar ? [y1, y2] : w.config.chart.stackType === "100%" ? series[realIndex][j] : w.seriesData.series[realIndex][j],
i: realIndex,
j,
barWidth,
barHeight,
textRects,
dataLabelsConfig
});
if (w.config.chart.stacked && barTotalDataLabelsConfig.enabled) {
totalDataLabels = this.drawTotalDataLabels({
x: dataLabelsPos.totalDataLabelsX,
y: dataLabelsPos.totalDataLabelsY,
barWidth,
barHeight,
realIndex,
textAnchor: dataLabelsPos.totalDataLabelsAnchor,
val: this.getStackedTotalDataLabel({ realIndex, j }),
dataLabelsConfig,
barTotalDataLabelsConfig
});
}
return {
dataLabelsPos,
dataLabels,
totalDataLabels
};
}
/** @param {{realIndex: any, j: any}} opts */
getStackedTotalDataLabel({ realIndex, j }) {
const w = this.w;
let val = this.barCtx.stackedSeriesTotals[j];
if (this.totalFormatter) {
val = this.totalFormatter(val, __spreadProps(__spreadValues({}, w), {
seriesIndex: realIndex,
dataPointIndex: j,
w
}));
}
return val;
}
/**
* @param {Record<string, any>} opts
*/
calculateColumnsDataLabelsPosition(opts) {
const w = this.w;
let {
i,
j,
realIndex,
y,
bcx,
barWidth,
barHeight,
textRects,
dataLabelsX,
dataLabelsY,
dataLabelsConfig,
barDataLabelsConfig,
barTotalDataLabelsConfig,
strokeWidth,
offX,
offY
} = opts;
let totalDataLabelsY;
let totalDataLabelsX;
const totalDataLabelsAnchor = "middle";
const totalDataLabelsBcx = bcx;
barHeight = Math.abs(barHeight);
const vertical = w.config.plotOptions.bar.dataLabels.orientation === "vertical";
const { zeroEncounters } = this.barCtx.barHelpers.getZeroValueEncounters({
i,
j
});
bcx = bcx - strokeWidth / 2;
const dataPointsDividedWidth = w.layout.gridWidth / w.globals.dataPoints;
if (this.barCtx.isVerticalGroupedRangeBar) {
dataLabelsX += barWidth / 2;
} else {
if (w.axisFlags.isXNumeric) {
dataLabelsX = bcx - barWidth / 2 + offX;
} else {
dataLabelsX = bcx - dataPointsDividedWidth + barWidth / 2 + offX;
}
if (!w.config.chart.stacked && zeroEncounters > 0 && w.config.plotOptions.bar.hideZeroBarsWhenGrouped) {
dataLabelsX -= barWidth * zeroEncounters;
}
}
if (vertical) {
const offsetDLX = 2;
dataLabelsX = dataLabelsX + textRects.height / 2 - strokeWidth / 2 - offsetDLX;
}
const valIsNegative = w.seriesData.series[i][j] < 0;
let newY = y;
if (this.barCtx.isReversed) {
newY = y + (valIsNegative ? barHeight : -barHeight);
}
switch (barDataLabelsConfig.position) {
case "center":
if (vertical) {
if (valIsNegative) {
dataLabelsY = newY - barHeight / 2 + offY;
} else {
dataLabelsY = newY + barHeight / 2 - offY;
}
} else {
if (valIsNegative) {
dataLabelsY = newY - barHeight / 2 + textRects.height / 2 + offY;
} else {
dataLabelsY = newY + barHeight / 2 + textRects.height / 2 - offY;
}
}
break;
case "bottom":
if (vertical) {
if (valIsNegative) {
dataLabelsY = newY - barHeight + offY;
} else {
dataLabelsY = newY + barHeight - offY;
}
} else {
if (valIsNegative) {
dataLabelsY = newY - barHeight + textRects.height + strokeWidth + offY;
} else {
dataLabelsY = newY + barHeight - textRects.height / 2 + strokeWidth - offY;
}
}
break;
case "top":
if (vertical) {
if (valIsNegative) {
dataLabelsY = newY + offY;
} else {
dataLabelsY = newY - offY;
}
} else {
if (valIsNegative) {
dataLabelsY = newY - textRects.height / 2 - offY;
} else {
dataLabelsY = newY + textRects.height + offY;
}
}
break;
}
let lowestPrevY = newY;
w.labelData.seriesGroups.forEach((sg) => {
var _a;
(_a = this.barCtx[sg.join(",")]) == null ? void 0 : _a.prevY.forEach(
(arr) => {
if (valIsNegative) {
lowestPrevY = Math.max(arr[j], lowestPrevY);
} else {
lowestPrevY = Math.min(arr[j], lowestPrevY);
}
}
);
});
if (this.barCtx.lastActiveBarSerieIndex === realIndex && barTotalDataLabelsConfig.enabled) {
const ADDITIONAL_OFFY = 18;
const graphics = new Graphics(this.barCtx.w);
const totalLabeltextRects = graphics.getTextRects(
this.getStackedTotalDataLabel({ realIndex, j }),
dataLabelsConfig.fontSize
);
if (valIsNegative) {
totalDataLabelsY = lowestPrevY - totalLabeltextRects.height / 2 - offY - barTotalDataLabelsConfig.offsetY + ADDITIONAL_OFFY;
} else {
totalDataLabelsY = lowestPrevY + totalLabeltextRects.height + offY + barTotalDataLabelsConfig.offsetY - ADDITIONAL_OFFY;
}
const xDivision = dataPointsDividedWidth;
totalDataLabelsX = totalDataLabelsBcx + (w.axisFlags.isXNumeric ? -barWidth * w.globals.barGroups.length / 2 : w.globals.barGroups.length * barWidth / 2 - (w.globals.barGroups.length - 1) * barWidth - xDivision) + barTotalDataLabelsConfig.offsetX;
}
if (!w.config.chart.stacked) {
if (dataLabelsY < 0) {
dataLabelsY = 0 + strokeWidth;
} else if (dataLabelsY + textRects.height / 3 > w.layout.gridHeight) {
dataLabelsY = w.layout.gridHeight - strokeWidth;
}
}
return {
bcx,
bcy: y,
dataLabelsX,
dataLabelsY,
totalDataLabelsX,
totalDataLabelsY,
totalDataLabelsAnchor
};
}
/**
* @param {Record<string, any>} opts
*/
calculateBarsDataLabelsPosition(opts) {
const w = this.w;
let {
x,
i,
j,
realIndex,
bcy,
barHeight,
barWidth,
textRects,
dataLabelsX,
strokeWidth,
dataLabelsConfig,
barDataLabelsConfig,
barTotalDataLabelsConfig,
offX,
offY
} = opts;
const dataPointsDividedHeight = w.layout.gridHeight / w.globals.dataPoints;
const { zeroEncounters } = this.barCtx.barHelpers.getZeroValueEncounters({
i,
j
});
barWidth = Math.abs(barWidth);
let dataLabelsY = bcy - (this.barCtx.isRangeBar ? 0 : dataPointsDividedHeight) + barHeight / 2 + textRects.height / 2 + offY - 3;
if (!w.config.chart.stacked && zeroEncounters > 0 && w.config.plotOptions.bar.hideZeroBarsWhenGrouped) {
dataLabelsY -= barHeight * zeroEncounters;
}
let totalDataLabelsX;
let totalDataLabelsY;
let totalDataLabelsAnchor = "start";
const valIsNegative = w.seriesData.series[i][j] < 0;
let newX = x;
if (this.barCtx.isReversed) {
newX = x + (valIsNegative ? -barWidth : barWidth);
totalDataLabelsAnchor = valIsNegative ? "start" : "end";
}
switch (barDataLabelsConfig.position) {
case "center":
if (valIsNegative) {
dataLabelsX = newX + barWidth / 2 - offX;
} else {
dataLabelsX = Math.max(textRects.width / 2, newX - barWidth / 2) + offX;
}
break;
case "bottom":
if (valIsNegative) {
dataLabelsX = newX + barWidth - strokeWidth - offX;
} else {
dataLabelsX = newX - barWidth + strokeWidth + offX;
}
break;
case "top":
if (valIsNegative) {
dataLabelsX = newX - strokeWidth - offX;
} else {
dataLabelsX = newX - strokeWidth + offX;
}
break;
}
let lowestPrevX = newX;
w.labelData.seriesGroups.forEach((sg) => {
var _a;
(_a = this.barCtx[sg.join(",")]) == null ? void 0 : _a.prevX.forEach(
(arr) => {
if (valIsNegative) {
lowestPrevX = Math.min(arr[j], lowestPrevX);
} else {
lowestPrevX = Math.max(arr[j], lowestPrevX);
}
}
);
});
if (this.barCtx.lastActiveBarSerieIndex === realIndex && barTotalDataLabelsConfig.enabled) {
const graphics = new Graphics(this.barCtx.w);
const totalLabeltextRects = graphics.getTextRects(
this.getStackedTotalDataLabel({ realIndex, j }),
dataLabelsConfig.fontSize
);
if (valIsNegative) {
totalDataLabelsX = lowestPrevX - strokeWidth - offX - barTotalDataLabelsConfig.offsetX;
totalDataLabelsAnchor = "end";
} else {
totalDataLabelsX = lowestPrevX + offX + barTotalDataLabelsConfig.offsetX + (this.barCtx.isReversed ? -(barWidth + strokeWidth) : strokeWidth);
}
totalDataLabelsY = dataLabelsY - textRects.height / 2 + totalLabeltextRects.height / 2 + barTotalDataLabelsConfig.offsetY + strokeWidth;
if (w.globals.barGroups.length > 1) {
totalDataLabelsY = totalDataLabelsY - w.globals.barGroups.length / 2 * (barHeight / 2);
}
}
if (!w.config.chart.stacked) {
if (dataLabelsConfig.textAnchor === "start") {
if (dataLabelsX - textRects.width < 0) {
dataLabelsX = valIsNegative ? textRects.width + strokeWidth : strokeWidth;
} else if (dataLabelsX + textRects.width > w.layout.gridWidth) {
dataLabelsX = valIsNegative ? w.layout.gridWidth - strokeWidth : w.layout.gridWidth - textRects.width - strokeWidth;
}
} else if (dataLabelsConfig.textAnchor === "middle") {
if (dataLabelsX - textRects.width / 2 < 0) {
dataLabelsX = textRects.width / 2 + strokeWidth;
} else if (dataLabelsX + textRects.width / 2 > w.layout.gridWidth) {
dataLabelsX = w.layout.gridWidth - textRects.width / 2 - strokeWidth;
}
} else if (dataLabelsConfig.textAnchor === "end") {
if (dataLabelsX < 1) {
dataLabelsX = textRects.width + strokeWidth;
} else if (dataLabelsX + 1 > w.layout.gridWidth) {
dataLabelsX = w.layout.gridWidth - textRects.width - strokeWidth;
}
}
}
return {
bcx: x,
bcy,
dataLabelsX,
dataLabelsY,
totalDataLabelsX,
totalDataLabelsY,
totalDataLabelsAnchor
};
}
/** @param {{x: any, y: any, val: any, i: any, j: any, textRects: any, barHeight: any, barWidth: any, dataLabelsConfig: any}} opts */
drawCalculatedDataLabels({
x,
y,
val,
i,
// = realIndex
j,
textRects,
barHeight,
barWidth,
dataLabelsConfig
}) {
const w = this.w;
let rotate = "rotate(0)";
if (w.config.plotOptions.bar.dataLabels.orientation === "vertical")
rotate = `rotate(-90, ${x}, ${y})`;
const dataLabels = new DataLabels(this.barCtx.w, this.barCtx.ctx);
const graphics = new Graphics(this.barCtx.w);
const formatter = dataLabelsConfig.formatter;
let elDataLabelsWrap = null;
const isSeriesNotCollapsed = w.globals.collapsedSeriesIndices.indexOf(i) > -1;
if (dataLabelsConfig.enabled && !isSeriesNotCollapsed) {
elDataLabelsWrap = graphics.group({
class: "apexcharts-data-labels",
transform: rotate
});
let text = "";
if (typeof val !== "undefined") {
text = formatter(val, __spreadProps(__spreadValues({}, w), {
seriesIndex: i,
dataPointIndex: j,
w
}));
}
if (!val && w.config.plotOptions.bar.hideZeroBarsWhenGrouped) {
text = "";
}
const valIsNegative = w.seriesData.series[i][j] < 0;
const position = w.config.plotOptions.bar.dataLabels.position;
if (w.config.plotOptions.bar.dataLabels.orientation === "vertical") {
if (position === "top") {
if (valIsNegative) dataLabelsConfig.textAnchor = "end";
else dataLabelsConfig.textAnchor = "start";
}
if (position === "center") {
dataLabelsConfig.textAnchor = "middle";
}
if (position === "bottom") {
if (valIsNegative) dataLabelsConfig.textAnchor = "end";
else dataLabelsConfig.textAnchor = "start";
}
}
if (this.barCtx.isRangeBar && this.barCtx.barOptions.dataLabels.hideOverflowingLabels) {
const txRect = graphics.getTextRects(
text,
parseFloat(dataLabelsConfig.style.fontSize).toString()
);
if (barWidth < txRect.width) {
text = "";
}
}
if (w.config.chart.stacked && this.barCtx.barOptions.dataLabels.hideOverflowingLabels) {
if (this.barCtx.isHorizontal) {
if (textRects.width / 1.6 > Math.abs(barWidth)) {
text = "";
}
} else {
if (textRects.height / 1.6 > Math.abs(barHeight)) {
text = "";
}
}
}
const modifiedDataLabelsConfig = __spreadValues({}, dataLabelsConfig);
if (this.barCtx.isHorizontal) {
if (val < 0) {
if (dataLabelsConfig.textAnchor === "start") {
modifiedDataLabelsConfig.textAnchor = "end";
} else if (dataLabelsConfig.textAnchor === "end") {
modifiedDataLabelsConfig.textAnchor = "start";
}
}
}
dataLabels.plotDataLabelsText({
x,
y,
text,
i,
j,
parent: elDataLabelsWrap,
dataLabelsConfig: modifiedDataLabelsConfig,
alwaysDrawDataLabel: true,
offsetCorrection: true
});
}
return elDataLabelsWrap;
}
/** @param {{ x?: any, y?: any, val?: any, realIndex?: any, textAnchor?: any, barWidth?: any, barHeight?: any, dataLabelsConfig?: any, barTotalDataLabelsConfig?: any }} opts */
drawTotalDataLabels({
x,
y,
val,
realIndex,
textAnchor,
barTotalDataLabelsConfig
}) {
const graphics = new Graphics(this.barCtx.w);
let totalDataLabelText;
if (barTotalDataLabelsConfig.enabled && typeof x !== "undefined" && typeof y !== "undefined" && this.barCtx.lastActiveBarSerieIndex === realIndex) {
totalDataLabelText = graphics.drawText({
x,
y,
foreColor: barTotalDataLabelsConfig.style.color,
text: val,
textAnchor,
fontFamily: barTotalDataLabelsConfig.style.fontFamily,
fontSize: barTotalDataLabelsConfig.style.fontSize,
fontWeight: barTotalDataLabelsConfig.style.fontWeight
});
}
return totalDataLabelText;
}
}
let Helpers$1 = class Helpers4 {
/**
* @param {Record<string, any>} barCtx
*/
constructor(barCtx) {
this.w = barCtx.w;
this.barCtx = barCtx;
}
/**
* @param {any[]} series
*/
initVariables(series) {
const w = this.w;
this.barCtx.series = series;
this.barCtx.totalItems = 0;
this.barCtx.seriesLen = 0;
this.barCtx.visibleI = -1;
this.barCtx.visibleItems = 1;
for (let sl = 0; sl < series.length; sl++) {
if (series[sl].length > 0) {
this.barCtx.seriesLen = this.barCtx.seriesLen + 1;
this.barCtx.totalItems += series[sl].length;
}
if (w.axisFlags.isXNumeric) {
for (let j = 0; j < series[sl].length; j++) {
if (w.seriesData.seriesX[sl][j] > w.globals.minX && w.seriesData.seriesX[sl][j] < w.globals.maxX) {
this.barCtx.visibleItems++;
}
}
} else {
this.barCtx.visibleItems = w.globals.dataPoints;
}
}
this.arrBorderRadius = this.createBorderRadiusArr(w.seriesData.series);
if (Utils$1.isSafari()) {
this.arrBorderRadius = this.arrBorderRadius.map(
(brArr) => (
/**
* @param {any} _
*/
brArr.map((_) => "none")
)
);
}
if (this.barCtx.seriesLen === 0) {
this.barCtx.seriesLen = 1;
}
this.barCtx.zeroSerieses = [];
if (!w.globals.comboCharts) {
this.checkZeroSeries({ series });
}
}
/**
* @param {number} realIndex
*/
initialPositions(realIndex) {
const w = this.w;
let x, y, yDivision, xDivision, barHeight, barWidth, zeroH, zeroW;
let dataPoints = w.globals.dataPoints;
if (this.barCtx.isRangeBar) {
dataPoints = w.labelData.labels.length;
}
let seriesLen = this.barCtx.seriesLen;
if (w.config.plotOptions.bar.rangeBarGroupRows) {
seriesLen = 1;
}
if (this.barCtx.isHorizontal) {
yDivision = w.layout.gridHeight / dataPoints;
barHeight = yDivision / seriesLen;
if (w.axisFlags.isXNumeric) {
yDivision = w.layout.gridHeight / this.barCtx.totalItems;
barHeight = yDivision / this.barCtx.seriesLen;
}
barHeight = barHeight * parseInt(this.barCtx.barOptions.barHeight, 10) / 100;
if (String(this.barCtx.barOptions.barHeight).indexOf("%") === -1) {
barHeight = parseInt(this.barCtx.barOptions.barHeight, 10);
}
zeroW = this.barCtx.baseLineInvertedY + w.globals.padHorizontal + (this.barCtx.isReversed ? w.layout.gridWidth : 0) - (this.barCtx.isReversed ? this.barCtx.baseLineInvertedY * 2 : 0);
if (this.barCtx.isFunnel) {
zeroW = w.layout.gridWidth / 2;
}
y = (yDivision - barHeight * this.barCtx.seriesLen) / 2;
} else {
xDivision = w.layout.gridWidth / this.barCtx.visibleItems;
if (w.config.xaxis.convertedCatToNumeric) {
xDivision = w.layout.gridWidth / w.globals.dataPoints;
}
barWidth = xDivision / seriesLen * parseInt(this.barCtx.barOptions.columnWidth, 10) / 100;
if (w.axisFlags.isXNumeric) {
const xRatio = this.barCtx.xRatio;
if (w.globals.minXDiff && w.globals.minXDiff !== 0.5 && w.globals.minXDiff / xRatio > 0) {
xDivision = w.globals.minXDiff / xRatio;
}
barWidth = xDivision / seriesLen * parseInt(this.barCtx.barOptions.columnWidth, 10) / 100;
if (barWidth < 1) {
barWidth = 1;
}
}
if (String(this.barCtx.barOptions.columnWidth).indexOf("%") === -1) {
barWidth = parseInt(this.barCtx.barOptions.columnWidth, 10);
}
zeroH = w.layout.gridHeight - this.barCtx.baseLineY[this.barCtx.translationsIndex] - (this.barCtx.isReversed ? w.layout.gridHeight : 0) + (this.barCtx.isReversed ? this.barCtx.baseLineY[this.barCtx.translationsIndex] * 2 : 0);
if (w.axisFlags.isXNumeric) {
const xForNumericX = this.barCtx.getBarXForNumericXAxis({
x,
j: 0,
realIndex,
barWidth
});
x = xForNumericX.x;
} else {
x = w.globals.padHorizontal + Utils$1.noExponents(xDivision - barWidth * this.barCtx.seriesLen) / 2;
}
}
w.globals.barHeight = barHeight;
w.globals.barWidth = barWidth;
return {
x,
y,
yDivision,
xDivision,
barHeight,
barWidth,
zeroH,
zeroW
};
}
/**
* @param {Record<string, any>} ctx
*/
initializeStackedPrevVars(ctx) {
const w = ctx.w;
w.labelData.seriesGroups.forEach((group) => {
if (!ctx[group]) ctx[group] = {};
ctx[group].prevY = [];
ctx[group].prevX = [];
ctx[group].prevYF = [];
ctx[group].prevXF = [];
ctx[group].prevYVal = [];
ctx[group].prevXVal = [];
});
}
/**
* @param {Record<string, any>} ctx
*/
initializeStackedXYVars(ctx) {
const w = ctx.w;
w.labelData.seriesGroups.forEach((group) => {
if (!ctx[group]) ctx[group] = {};
ctx[group].xArrj = [];
ctx[group].xArrjF = [];
ctx[group].xArrjVal = [];
ctx[group].yArrj = [];
ctx[group].yArrjF = [];
ctx[group].yArrjVal = [];
});
}
/**
* @param {any[]} series
* @param {number} i
* @param {number} j
* @param {number} realIndex
*/
getPathFillColor(series, i, j, realIndex) {
var _a, _b, _c, _d;
const w = this.w;
const fill = new Fill(this.barCtx.w);
let fillColor = null;
const seriesNumber = this.barCtx.barOptions.distributed ? j : i;
let useRangeColor = false;
if (this.barCtx.barOptions.colors.ranges.length > 0) {
const colorRange = this.barCtx.barOptions.colors.ranges;
colorRange.map((range) => {
if (series[i][j] >= range.from && series[i][j] <= range.to) {
fillColor = range.color;
useRangeColor = true;
}
});
}
const pathFill = fill.fillPath({
seriesNumber: this.barCtx.barOptions.distributed ? seriesNumber : realIndex,
dataPointIndex: j,
color: fillColor,
value: series[i][j],
fillConfig: (_a = w.config.series[i].data[j]) == null ? void 0 : _a.fill,
fillType: ((_c = (_b = w.config.series[i].data[j]) == null ? void 0 : _b.fill) == null ? void 0 : _c.type) ? (_d = w.config.series[i].data[j]) == null ? void 0 : _d.fill.type : Array.isArray(w.config.fill.type) ? w.config.fill.type[realIndex] : w.config.fill.type
});
return {
color: pathFill,
useRangeColor
};
}
/**
* @param {number} i
* @param {number} j
* @param {number} realIndex
*/
getStrokeWidth(i, j, realIndex) {
let strokeWidth = 0;
const w = this.w;
if (typeof this.barCtx.series[i][j] === "undefined" || this.barCtx.series[i][j] === null || w.config.chart.type === "bar" && !this.barCtx.series[i][j]) {
this.barCtx.isNullValue = true;
} else {
this.barCtx.isNullValue = false;
}
if (w.config.stroke.show) {
if (!this.barCtx.isNullValue) {
strokeWidth = Array.isArray(this.barCtx.strokeWidth) ? this.barCtx.strokeWidth[realIndex] : this.barCtx.strokeWidth;
}
}
return strokeWidth;
}
/**
* @param {any[]} series
*/
createBorderRadiusArr(series) {
var _a;
const w = this.w;
const alwaysApplyRadius = !this.w.config.chart.stacked || w.config.plotOptions.bar.borderRadius <= 0;
const numSeries = series.length;
const numColumns = ((_a = series[0]) == null ? void 0 : _a.length) | 0;
const output = Array.from(
{ length: numSeries },
() => Array(numColumns).fill(alwaysApplyRadius ? "top" : "none")
);
if (alwaysApplyRadius) return output;
const chartType = this.w.config.chart.type;
for (let j = 0; j < numColumns; j++) {
const positiveIndices = [];
const negativeIndices = [];
let nonZeroCount = 0;
for (let i = 0; i < numSeries; i++) {
const value = series[i][j];
if (value > 0) {
positiveIndices.push(i);
nonZeroCount++;
} else if (value < 0) {
negativeIndices.push(i);
nonZeroCount++;
}
}
if (positiveIndices.length > 0 && negativeIndices.length === 0) {
if (positiveIndices.length === 1) {
output[positiveIndices[0]][j] = chartType === "bar" && numColumns === 1 ? "top" : "both";
} else {
const firstPositiveIndex = positiveIndices[0];
const lastPositiveIndex = positiveIndices[positiveIndices.length - 1];
for (const i of positiveIndices) {
if (i === firstPositiveIndex) {
output[i][j] = chartType === "bar" && numColumns === 1 ? "top" : "bottom";
} else if (i === lastPositiveIndex) {
output[i][j] = "top";
} else {
output[i][j] = "none";
}
}
}
} else if (negativeIndices.length > 0 && positiveIndices.length === 0) {
if (negativeIndices.length === 1) {
output[negativeIndices[0]][j] = "both";
} else {
const highestNegativeIndex = Math.max(...negativeIndices);
const lowestNegativeIndex = Math.min(...negativeIndices);
for (const i of negativeIndices) {
if (i === highestNegativeIndex) {
output[i][j] = "bottom";
} else if (i === lowestNegativeIndex) {
output[i][j] = "top";
} else {
output[i][j] = "none";
}
}
}
} else if (positiveIndices.length > 0 && negativeIndices.length > 0) {
const lastPositiveIndex = positiveIndices[positiveIndices.length - 1];
for (const i of positiveIndices) {
if (i === lastPositiveIndex) {
output[i][j] = "top";
} else {
output[i][j] = "none";
}
}
const highestNegativeIndex = Math.max(...negativeIndices);
for (const i of negativeIndices) {
if (i === highestNegativeIndex) {
output[i][j] = "bottom";
} else {
output[i][j] = "none";
}
}
} else if (nonZeroCount === 1) {
const index = positiveIndices[0] || negativeIndices[0];
output[index][j] = "both";
}
}
return output;
}
/** @param {{ j?: any, i?: any, x1?: any, x2?: any, y1?: any, y2?: any, bc?: any, elSeries?: any }} opts */
barBackground({ j, i, x1, x2, y1, y2, elSeries }) {
const w = this.w;
const graphics = new Graphics(this.barCtx.w);
const sr = new Series(this.barCtx.w);
const activeSeriesIndex = sr.getActiveConfigSeriesIndex();
if (this.barCtx.barOptions.colors.backgroundBarColors.length > 0 && activeSeriesIndex === i) {
if (j >= this.barCtx.barOptions.colors.backgroundBarColors.length) {
j %= this.barCtx.barOptions.colors.backgroundBarColors.length;
}
const bcolor = this.barCtx.barOptions.colors.backgroundBarColors[j];
const rect = graphics.drawRect(
typeof x1 !== "undefined" ? x1 : 0,
typeof y1 !== "undefined" ? y1 : 0,
typeof x2 !== "undefined" ? x2 : w.layout.gridWidth,
typeof y2 !== "undefined" ? y2 : w.layout.gridHeight,
this.barCtx.barOptions.colors.backgroundBarRadius,
bcolor,
this.barCtx.barOptions.colors.backgroundBarOpacity
);
elSeries.add(rect);
rect.node.classList.add("apexcharts-backgroundBar");
}
}
/** @param {{ barWidth?: any, barXPosition?: any, y1?: any, y2?: any, yRatio?: any, strokeWidth?: any, isReversed?: any, series?: any, seriesGroup?: any, realIndex?: any, i?: any, j?: any, w?: any }} opts */
getColumnPaths({
barWidth,
barXPosition,
y1,
y2,
strokeWidth,
isReversed,
series,
seriesGroup,
realIndex,
i,
j,
w
}) {
var _a;
const graphics = new Graphics(this.barCtx.w);
strokeWidth = Array.isArray(strokeWidth) ? strokeWidth[realIndex] : strokeWidth;
if (!strokeWidth) strokeWidth = 0;
let bW = barWidth;
let bXP = barXPosition;
if ((_a = w.config.series[realIndex].data[j]) == null ? void 0 : _a.columnWidthOffset) {
bXP = barXPosition - w.config.series[realIndex].data[j].columnWidthOffset / 2;
bW = barWidth + w.config.series[realIndex].data[j].columnWidthOffset;
}
const strokeCenter = strokeWidth / 2;
const x1 = bXP + strokeCenter;
const x2 = bXP + bW - strokeCenter;
const direction = (series[i][j] >= 0 ? 1 : -1) * (isReversed ? -1 : 1);
y1 += 1e-3 - strokeCenter * direction;
y2 += 1e-3 + strokeCenter * direction;
let pathTo = graphics.move(x1, y1);
let pathFrom = graphics.move(x1, y1);
const sl = graphics.line(x2, y1);
if (w.globals.previousPaths.length > 0) {
pathFrom = this.barCtx.getPreviousPath(realIndex, j, false);
}
pathTo = pathTo + graphics.line(x1, y2) + graphics.line(x2, y2) + sl + (w.config.plotOptions.bar.borderRadiusApplication === "around" || this.arrBorderRadius[realIndex][j] === "both" ? " Z" : " z");
pathFrom = pathFrom + graphics.line(x1, y1) + sl + sl + sl + sl + sl + graphics.line(x1, y1) + (w.config.plotOptions.bar.borderRadiusApplication === "around" || this.arrBorderRadius[realIndex][j] === "both" ? " Z" : " z");
if (this.arrBorderRadius[realIndex][j] !== "none") {
pathTo = graphics.roundPathCorners(
pathTo,
w.config.plotOptions.bar.borderRadius
);
}
if (w.config.chart.stacked) {
let _ctx = this.barCtx;
_ctx = this.barCtx[seriesGroup];
_ctx.yArrj.push(y2 - strokeCenter * direction);
_ctx.yArrjF.push(Math.abs(y1 - y2 + strokeWidth * direction));
_ctx.yArrjVal.push(this.barCtx.series[i][j]);
}
return {
pathTo,
pathFrom
};
}
/** @param {{ barYPosition?: any, barHeight?: any, x1?: any, x2?: any, strokeWidth?: any, isReversed?: any, series?: any, seriesGroup?: any, realIndex?: any, i?: any, j?: any, w?: any }} opts */
getBarpaths({
barYPosition,
barHeight,
x1,
x2,
strokeWidth,
isReversed,
series,
seriesGroup,
realIndex,
i,
j,
w
}) {
var _a;
const graphics = new Graphics(this.barCtx.w);
strokeWidth = Array.isArray(strokeWidth) ? strokeWidth[realIndex] : strokeWidth;
if (!strokeWidth) strokeWidth = 0;
let bYP = barYPosition;
let bH = barHeight;
if ((_a = w.config.series[realIndex].data[j]) == null ? void 0 : _a.barHeightOffset) {
bYP = barYPosition - w.config.series[realIndex].data[j].barHeightOffset / 2;
bH = barHeight + w.config.series[realIndex].data[j].barHeightOffset;
}
const strokeCenter = strokeWidth / 2;
const y1 = bYP + strokeCenter;
const y2 = bYP + bH - strokeCenter;
const direction = (series[i][j] >= 0 ? 1 : -1) * (isReversed ? -1 : 1);
x1 += 1e-3 + strokeCenter * direction;
x2 += 1e-3 - strokeCenter * direction;
let pathTo = graphics.move(x1, y1);
let pathFrom = graphics.move(x1, y1);
if (w.globals.previousPaths.length > 0) {
pathFrom = this.barCtx.getPreviousPath(realIndex, j, false);
}
const sl = graphics.line(x1, y2);
pathTo = pathTo + graphics.line(x2, y1) + graphics.line(x2, y2) + sl + (w.config.plotOptions.bar.borderRadiusApplication === "around" || this.arrBorderRadius[realIndex][j] === "both" ? " Z" : " z");
pathFrom = pathFrom + graphics.line(x1, y1) + sl + sl + sl + sl + sl + graphics.line(x1, y1) + (w.config.plotOptions.bar.borderRadiusApplication === "around" || this.arrBorderRadius[realIndex][j] === "both" ? " Z" : " z");
if (this.arrBorderRadius[realIndex][j] !== "none") {
pathTo = graphics.roundPathCorners(
pathTo,
w.config.plotOptions.bar.borderRadius
);
}
if (w.config.chart.stacked) {
let _ctx = this.barCtx;
_ctx = this.barCtx[seriesGroup];
_ctx.xArrj.push(x2 + strokeCenter * direction);
_ctx.xArrjF.push(Math.abs(x1 - x2 - strokeWidth * direction));
_ctx.xArrjVal.push(this.barCtx.series[i][j]);
}
return {
pathTo,
pathFrom
};
}
/** @param {{series: any}} opts */
checkZeroSeries({ series }) {
const w = this.w;
for (let zs = 0; zs < series.length; zs++) {
let total = 0;
for (let zsj = 0; zsj < series[w.globals.maxValsInArrayIndex].length; zsj++) {
total += series[zs][zsj];
}
if (total === 0) {
this.barCtx.zeroSerieses.push(zs);
}
}
}
/**
* @param {number} value
* @param {number} zeroW
*/
getXForValue(value, zeroW, zeroPositionForNull = true) {
let xForVal = zeroPositionForNull ? zeroW : null;
if (typeof value !== "undefined" && value !== null) {
xForVal = zeroW + value / this.barCtx.invertedYRatio - (this.barCtx.isReversed ? value / this.barCtx.invertedYRatio : 0) * 2;
}
return xForVal;
}
/**
* @param {number} value
* @param {number} zeroH
* @param {number} translationsIndex
*/
getYForValue(value, zeroH, translationsIndex, zeroPositionForNull = true) {
let yForVal = zeroPositionForNull ? zeroH : null;
if (typeof value !== "undefined" && value !== null) {
yForVal = zeroH - value / this.barCtx.yRatio[translationsIndex] + (this.barCtx.isReversed ? value / this.barCtx.yRatio[translationsIndex] : 0) * 2;
}
return yForVal;
}
/**
* @param {string} type
* @param {number} zeroW
* @param {number} zeroH
* @param {number} i
* @param {number} j
* @param {number} translationsIndex
*/
getGoalValues(type, zeroW, zeroH, i, j, translationsIndex) {
const w = this.w;
const goals = [];
const pushGoal = (value, attrs) => {
goals.push({
[type]: type === "x" ? this.getXForValue(value, zeroW, false) : this.getYForValue(value, zeroH, translationsIndex, false),
attrs
});
};
if (w.seriesData.seriesGoals[i] && w.seriesData.seriesGoals[i][j] && Array.isArray(w.seriesData.seriesGoals[i][j])) {
w.seriesData.seriesGoals[i][j].forEach((goal) => {
pushGoal(goal.value, goal);
});
}
if (this.barCtx.barOptions.isDumbbell && w.rangeData.seriesRange.length) {
const colors = this.barCtx.barOptions.dumbbellColors ? this.barCtx.barOptions.dumbbellColors : w.globals.colors;
const commonAttrs = {
strokeHeight: type === "x" ? 0 : w.globals.markers.size[i],
strokeWidth: type === "x" ? w.globals.markers.size[i] : 0,
strokeDashArray: 0,
strokeLineCap: "round",
strokeColor: Array.isArray(colors[i]) ? colors[i][0] : colors[i]
};
pushGoal(w.rangeData.seriesRangeStart[i][j], commonAttrs);
pushGoal(w.rangeData.seriesRangeEnd[i][j], __spreadProps(__spreadValues({}, commonAttrs), {
strokeColor: Array.isArray(colors[i]) ? colors[i][1] : colors[i]
}));
}
return goals;
}
/** @param {{barXPosition: any, barYPosition: any, goalX: any, goalY: any, barWidth: any, barHeight: any}} opts */
drawGoalLine({
barXPosition,
barYPosition,
goalX,
goalY,
barWidth,
barHeight
}) {
const graphics = new Graphics(this.barCtx.w);
const lineGroup = graphics.group({
className: "apexcharts-bar-goals-groups"
});
lineGroup.node.classList.add("apexcharts-element-hidden");
this.barCtx.w.globals.delayedElements.push({
el: lineGroup.node
});
lineGroup.attr(
"clip-path",
`url(#gridRectMarkerMask${this.barCtx.w.globals.cuid})`
);
let line = null;
if (this.barCtx.isHorizontal) {
if (Array.isArray(goalX)) {
goalX.forEach((goal) => {
if (goal.x >= -1 && goal.x <= graphics.w.layout.gridWidth + 1) {
const sHeight = typeof goal.attrs.strokeHeight !== "undefined" ? goal.attrs.strokeHeight : barHeight / 2;
const y = barYPosition + sHeight + barHeight / 2;
line = graphics.drawLine(
goal.x,
y - sHeight * 2,
goal.x,
y,
goal.attrs.strokeColor ? goal.attrs.strokeColor : void 0,
goal.attrs.strokeDashArray,
goal.attrs.strokeWidth ? goal.attrs.strokeWidth : 2,
goal.attrs.strokeLineCap
);
lineGroup.add(line);
}
});
}
} else {
if (Array.isArray(goalY)) {
goalY.forEach((goal) => {
if (goal.y >= -1 && goal.y <= graphics.w.layout.gridHeight + 1) {
const sWidth = typeof goal.attrs.strokeWidth !== "undefined" ? goal.attrs.strokeWidth : barWidth / 2;
const x = barXPosition + sWidth + barWidth / 2;
line = graphics.drawLine(
x - sWidth * 2,
goal.y,
x,
goal.y,
goal.attrs.strokeColor ? goal.attrs.strokeColor : void 0,
goal.attrs.strokeDashArray,
goal.attrs.strokeHeight ? goal.attrs.strokeHeight : 2,
goal.attrs.strokeLineCap
);
lineGroup.add(line);
}
});
}
}
return lineGroup;
}
/** @param {{prevPaths: any, currPaths: any, color: any, realIndex: any, j: any}} opts */
drawBarShadow({ prevPaths, currPaths, color, realIndex, j }) {
const w = this.w;
const { x: prevX2, x1: prevX1, barYPosition: prevY1 } = prevPaths;
const { x: currX2, x1: currX1, barYPosition: currY1 } = currPaths;
const prevY2 = prevY1 + currPaths.barHeight;
const graphics = new Graphics(this.barCtx.w);
const utils = new Utils$1();
const shadowPath = graphics.move(prevX1, prevY2) + graphics.line(prevX2, prevY2) + graphics.line(currX2, currY1) + graphics.line(currX1, currY1) + graphics.line(prevX1, prevY2) + (w.config.plotOptions.bar.borderRadiusApplication === "around" || this.arrBorderRadius[realIndex][j] === "both" ? " Z" : " z");
return graphics.drawPath({
d: shadowPath,
fill: utils.shadeColor(0.5, Utils$1.rgb2hex(color)),
stroke: "none",
strokeWidth: 0,
fillOpacity: 1,
classes: "apexcharts-bar-shadow apexcharts-decoration-element"
});
}
/** @param {{i: any, j: any}} opts */
getZeroValueEncounters({ i, j }) {
var _a;
const w = this.w;
let nonZeroColumns = 0;
let zeroEncounters = 0;
const seriesIndices = w.config.plotOptions.bar.horizontal ? w.seriesData.series.map((_, _i) => _i) : ((_a = w.globals.columnSeries) == null ? void 0 : _a.i.map((_i) => _i)) || [];
seriesIndices.forEach((_si) => {
const val = w.globals.seriesPercent[_si][j];
if (val) {
nonZeroColumns++;
}
if (_si < i && val === 0) {
zeroEncounters++;
}
});
return {
nonZeroColumns,
zeroEncounters
};
}
/**
* @param {number} seriesIndex
*/
getGroupIndex(seriesIndex) {
const w = this.w;
const groupIndex = w.labelData.seriesGroups.findIndex(
(group) => (
// w.config.series[i].name may be undefined, so use
// w.seriesData.seriesNames[i], which has default names for those
// series. w.labelData.seriesGroups[] uses the same default naming.
group.indexOf(w.seriesData.seriesNames[seriesIndex]) > -1
)
);
const cGI = this.barCtx.columnGroupIndices;
let columnGroupIndex = cGI.indexOf(groupIndex);
if (columnGroupIndex < 0) {
cGI.push(groupIndex);
columnGroupIndex = cGI.length - 1;
}
return { groupIndex, columnGroupIndex };
}
};
class Bar {
/**
* @param {import('../types/internal').ChartStateW} w
* @param {import('../types/internal').ChartContext} ctx
* @param {import('../types/internal').XYRatios} xyRatios
*/
constructor(w, ctx, xyRatios) {
this.ctx = ctx;
this.w = w;
this.barOptions = w.config.plotOptions.bar;
this.isHorizontal = this.barOptions.horizontal;
this.strokeWidth = w.config.stroke.width;
this.isNullValue = false;
this.isRangeBar = w.rangeData.seriesRange.length && this.isHorizontal;
this.isVerticalGroupedRangeBar = !w.globals.isBarHorizontal && w.rangeData.seriesRange.length && w.config.plotOptions.bar.rangeBarGroupRows;
this.isFunnel = this.barOptions.isFunnel;
this.xyRatios = xyRatios;
this.xRatio = 0;
this.yRatio = [];
this.invertedXRatio = 0;
this.invertedYRatio = 0;
this.baseLineY = [];
this.baseLineInvertedY = 0;
if (this.xyRatios !== null) {
this.xRatio = xyRatios.xRatio;
this.yRatio = xyRatios.yRatio;
this.invertedXRatio = xyRatios.invertedXRatio;
this.invertedYRatio = xyRatios.invertedYRatio;
this.baseLineY = xyRatios.baseLineY;
this.baseLineInvertedY = xyRatios.baseLineInvertedY;
}
this.yaxisIndex = 0;
this.translationsIndex = 0;
this.seriesLen = 0;
this.pathArr = [];
this.series = [];
this.elSeries = null;
this.visibleI = 0;
this.isReversed = false;
const ser = new Series(this.w);
this.lastActiveBarSerieIndex = ser.getActiveConfigSeriesIndex("desc", [
"bar",
"column"
]);
this.columnGroupIndices = [];
const barSeriesIndices = ser.getBarSeriesIndices();
const coreUtils = new CoreUtils(this.w);
this.stackedSeriesTotals = coreUtils.getStackedSeriesTotals(
this.w.config.series.map((s, i) => {
return barSeriesIndices.indexOf(i) === -1 ? i : -1;
}).filter((s) => {
return s !== -1;
})
);
this.barHelpers = new Helpers$1(this);
}
/** primary draw method which is called on bar object
* @memberof Bar
* @param {any[]} series - user supplied series values
* @param {number} seriesIndex - the index by which series will be drawn on the svg
* @return {Element} element which is supplied to parent chart draw method for appending
**/
draw(series, seriesIndex) {
var _a;
const w = this.w;
const graphics = new Graphics(this.w);
const coreUtils = new CoreUtils(this.w);
series = coreUtils.getLogSeries(series);
this.series = series;
this.yRatio = coreUtils.getLogYRatios(this.yRatio);
this.barHelpers.initVariables(series);
const ret = graphics.group({
class: "apexcharts-bar-series apexcharts-plot-series"
});
if (w.config.dataLabels.enabled) {
if (this.totalItems > this.barOptions.dataLabels.maxItems) {
console.warn(
"WARNING: DataLabels are enabled but there are too many to display. This may cause performance issue when rendering - ApexCharts"
);
}
}
for (let i = 0, bc = 0; i < series.length; i++, bc++) {
let x, y;
const yArrj = [];
const xArrj = [];
const realIndex = w.globals.comboCharts ? (
/** @type {any} */
seriesIndex[i]
) : i;
const { columnGroupIndex } = this.barHelpers.getGroupIndex(realIndex);
const elSeries = graphics.group({
class: `apexcharts-series`,
rel: i + 1,
seriesName: Utils$1.escapeString(w.seriesData.seriesNames[realIndex]),
"data:realIndex": realIndex
});
Series.addCollapsedClassToSeries(this.w, elSeries, realIndex);
if (series[i].length > 0) {
this.visibleI = this.visibleI + 1;
}
if (this.yRatio.length > 1) {
this.yaxisIndex = w.globals.seriesYAxisReverseMap[realIndex];
this.translationsIndex = realIndex;
}
const translationsIndex = this.translationsIndex;
this.isReversed = w.config.yaxis[this.yaxisIndex] && w.config.yaxis[this.yaxisIndex].reversed;
const initPositions = this.barHelpers.initialPositions(realIndex);
const {
y: initY,
yDivision,
// yDivision is the GRIDHEIGHT divided by number of datapoints (bars)
zeroW,
// zeroW is the baseline where 0 meets x axis
x: initX,
xDivision,
// xDivision is the GRIDWIDTH divided by number of datapoints (columns)
zeroH
// zeroH is the baseline where 0 meets y axis
} = initPositions;
let barHeight = initPositions.barHeight;
let barWidth = initPositions.barWidth;
y = initY;
x = initX;
if (!this.isHorizontal) {
xArrj.push(x + (barWidth != null ? barWidth : 0) / 2);
}
const elDataLabelsWrap = graphics.group({
class: "apexcharts-datalabels",
"data:realIndex": realIndex
});
w.globals.delayedElements.push({
el: elDataLabelsWrap.node
});
elDataLabelsWrap.node.classList.add("apexcharts-element-hidden");
const elGoalsMarkers = graphics.group({
class: "apexcharts-bar-goals-markers"
});
const elBarShadows = graphics.group({
class: "apexcharts-bar-shadows"
});
w.globals.delayedElements.push({
el: elBarShadows.node
});
elBarShadows.node.classList.add("apexcharts-element-hidden");
for (let j = 0; j < series[i].length; j++) {
const strokeWidth = this.barHelpers.getStrokeWidth(i, j, realIndex);
let paths = (
/** @type {any} */
null
);
const pathsParams = {
indexes: {
i,
j,
realIndex,
translationsIndex,
bc
},
x,
y,
strokeWidth,
elSeries
};
if (this.isHorizontal) {
paths = this.drawBarPaths(__spreadProps(__spreadValues({}, pathsParams), {
barHeight,
zeroW,
yDivision
}));
barWidth = this.series[i][j] / this.invertedYRatio;
} else {
paths = this.drawColumnPaths(__spreadProps(__spreadValues({}, pathsParams), {
xDivision,
barWidth,
zeroH
}));
barHeight = this.series[i][j] / this.yRatio[translationsIndex];
}
const pathFill = this.barHelpers.getPathFillColor(
series,
i,
j,
realIndex
);
if (this.isFunnel && this.barOptions.isFunnel3d && this.pathArr.length && j > 0) {
const barShadow = this.barHelpers.drawBarShadow({
color: typeof pathFill.color === "string" && ((_a = pathFill.color) == null ? void 0 : _a.indexOf("url")) === -1 ? pathFill.color : Utils$1.hexToRgba(w.globals.colors[i]),
prevPaths: this.pathArr[this.pathArr.length - 1],
currPaths: paths,
realIndex,
j
});
elBarShadows.add(barShadow);
if (w.config.chart.dropShadow.enabled) {
const filters = new Filters(this.w);
filters.dropShadow(barShadow, w.config.chart.dropShadow, realIndex);
}
}
this.pathArr.push(paths);
const barGoalLine = this.barHelpers.drawGoalLine({
barXPosition: paths.barXPosition,
barYPosition: paths.barYPosition,
goalX: paths.goalX,
goalY: paths.goalY,
barHeight,
barWidth
});
if (barGoalLine) {
elGoalsMarkers.add(barGoalLine);
}
y = paths.y;
x = paths.x;
if (j > 0) {
xArrj.push(x + (barWidth != null ? barWidth : 0) / 2);
}
yArrj.push(y);
this.renderSeries(__spreadProps(__spreadValues({
realIndex,
pathFill: pathFill.color
}, pathFill.useRangeColor ? { lineFill: pathFill.color } : {}), {
j,
i,
columnGroupIndex,
pathFrom: paths.pathFrom,
pathTo: paths.pathTo,
strokeWidth,
elSeries,
x,
y,
series,
barHeight: Math.abs(paths.barHeight ? paths.barHeight : barHeight),
barWidth: Math.abs(paths.barWidth ? paths.barWidth : barWidth),
elDataLabelsWrap,
elGoalsMarkers,
elBarShadows,
visibleSeries: this.visibleI,
type: "bar"
}));
}
w.globals.seriesXvalues[realIndex] = xArrj;
w.globals.seriesYvalues[realIndex] = yArrj;
ret.add(elSeries);
}
return ret;
}
/** @param {{ realIndex?: any, pathFill?: any, lineFill?: any, j?: any, i?: any, columnGroupIndex?: any, pathFrom?: any, pathTo?: any, strokeWidth?: any, elSeries?: any, x?: any, y?: any, y1?: any, y2?: any, series?: any, barHeight?: any, barWidth?: any, barXPosition?: any, barYPosition?: any, elDataLabelsWrap?: any, elGoalsMarkers?: any, elBarShadows?: any, visibleSeries?: any, type?: any, classes?: any }} opts */
renderSeries({
realIndex,
pathFill,
lineFill,
j,
i,
columnGroupIndex,
pathFrom,
pathTo,
strokeWidth,
elSeries,
x,
// x pos
y,
// y pos
y1,
// absolute value
y2,
// absolute value
series,
barHeight,
barWidth,
barXPosition,
barYPosition,
elDataLabelsWrap,
elGoalsMarkers,
elBarShadows,
visibleSeries,
type,
classes
}) {
const w = this.w;
const graphics = new Graphics(this.w, this.ctx);
let skipDrawing = false;
if (!elSeries._bindingsDelegated) {
elSeries._bindingsDelegated = true;
graphics.setupEventDelegation(elSeries, `.apexcharts-${type}-area`);
}
if (!lineFill) {
let fetchColor = function(i2) {
const exp = w.config.stroke.colors;
let c;
if (Array.isArray(exp) && exp.length > 0) {
c = exp[i2];
if (!c) c = "";
if (typeof c === "function") {
return c({
value: w.seriesData.series[i2][j],
dataPointIndex: j,
w
});
}
}
return c;
};
const checkAvailableColor = typeof w.globals.stroke.colors[realIndex] === "function" ? fetchColor(realIndex) : w.globals.stroke.colors[realIndex];
lineFill = this.barOptions.distributed ? w.globals.stroke.colors[j] : checkAvailableColor;
}
const barDataLabels = new BarDataLabels(this);
const dataLabelsObj = (
/** @type {any} */
barDataLabels.handleBarDataLabels({
x,
y,
y1,
y2,
i,
j,
series,
realIndex,
columnGroupIndex,
barHeight,
barWidth,
barXPosition,
barYPosition,
visibleSeries
})
);
if (!w.globals.isBarHorizontal) {
if (dataLabelsObj.dataLabelsPos.dataLabelsX + Math.max(barWidth, w.globals.barPadForNumericAxis) < 0 || dataLabelsObj.dataLabelsPos.dataLabelsX - Math.max(barWidth, w.globals.barPadForNumericAxis) > w.layout.gridWidth) {
skipDrawing = true;
}
}
if (
/** @type {Record<string,any>} */
w.config.series[i].data[j] && /** @type {Record<string,any>} */
w.config.series[i].data[j].strokeColor
) {
lineFill = /** @type {Record<string,any>} */
w.config.series[i].data[j].strokeColor;
}
if (this.isNullValue) {
pathFill = "none";
}
const delay = j / w.config.chart.animations.animateGradually.delay * (w.config.chart.animations.speed / w.globals.dataPoints) / 2.4;
if (!skipDrawing) {
const renderedPath = (
/** @type {any} */
graphics.renderPaths({
i,
j,
realIndex,
pathFrom,
pathTo,
stroke: lineFill,
strokeWidth,
strokeLineCap: w.config.stroke.lineCap,
fill: pathFill,
animationDelay: delay,
initialSpeed: w.config.chart.animations.speed,
dataChangeSpeed: w.config.chart.animations.dynamicAnimation.speed,
className: `apexcharts-${type}-area ${classes}`,
chartType: type,
bindEventsOnPaths: false
})
);
renderedPath.attr("clip-path", `url(#gridRectBarMask${w.globals.cuid})`);
const forecast = w.config.forecastDataPoints;
if (forecast.count > 0) {
if (j >= w.globals.dataPoints - forecast.count) {
renderedPath.node.setAttribute("stroke-dasharray", forecast.dashArray);
renderedPath.node.setAttribute("stroke-width", forecast.strokeWidth);
renderedPath.node.setAttribute("fill-opacity", forecast.fillOpacity);
}
}
if (typeof y1 !== "undefined" && typeof y2 !== "undefined") {
renderedPath.attr("data-range-y1", y1);
renderedPath.attr("data-range-y2", y2);
}
const filters = new Filters(this.w);
filters.setSelectionFilter(renderedPath, realIndex, j);
elSeries.add(renderedPath);
renderedPath.attr({
cy: dataLabelsObj.dataLabelsPos.bcy,
cx: dataLabelsObj.dataLabelsPos.bcx,
j,
val: w.seriesData.series[i][j],
barHeight,
barWidth
});
if (dataLabelsObj.dataLabels !== null) {
elDataLabelsWrap.add(dataLabelsObj.dataLabels);
}
if (dataLabelsObj.totalDataLabels) {
elDataLabelsWrap.add(dataLabelsObj.totalDataLabels);
}
elSeries.add(elDataLabelsWrap);
if (elGoalsMarkers) {
elSeries.add(elGoalsMarkers);
}
if (elBarShadows) {
elSeries.add(elBarShadows);
}
}
return elSeries;
}
/** @param {{indexes: any, barHeight: any, strokeWidth: any, zeroW: any, x: any, y: any, yDivision: any, elSeries: any}} opts */
drawBarPaths({
indexes,
barHeight,
strokeWidth,
zeroW,
x,
y,
yDivision,
elSeries
}) {
const w = this.w;
const i = indexes.i;
const j = indexes.j;
let barYPosition;
if (w.axisFlags.isXNumeric) {
y = (w.seriesData.seriesX[i][j] - w.globals.minX) / this.invertedXRatio - barHeight;
barYPosition = y + barHeight * this.visibleI;
} else {
if (w.config.plotOptions.bar.hideZeroBarsWhenGrouped) {
const { nonZeroColumns, zeroEncounters } = this.barHelpers.getZeroValueEncounters({ i, j });
if (nonZeroColumns > 0) {
barHeight = this.seriesLen * barHeight / nonZeroColumns;
}
barYPosition = y + barHeight * this.visibleI;
barYPosition -= barHeight * zeroEncounters;
} else {
barYPosition = y + barHeight * this.visibleI;
}
}
if (this.isFunnel) {
const _zeroW = zeroW != null ? zeroW : 0;
zeroW = _zeroW - /** @type {number} */
/** @type {any} */
(this.barHelpers.getXForValue(
/** @type {any} */
this.series[i][j],
_zeroW
) - _zeroW) / 2;
}
x = this.barHelpers.getXForValue(
/** @type {any} */
this.series[i][j],
zeroW != null ? zeroW : 0
);
const paths = (
/** @type {any} */
this.barHelpers.getBarpaths({
barYPosition,
barHeight,
x1: zeroW,
x2: x,
strokeWidth,
isReversed: this.isReversed,
series: this.series,
realIndex: indexes.realIndex,
i,
j,
w
})
);
if (!w.axisFlags.isXNumeric) {
y = y + yDivision;
}
this.barHelpers.barBackground({
j,
i,
y1: barYPosition - barHeight * this.visibleI,
y2: barHeight * this.seriesLen,
elSeries
});
return {
pathTo: paths.pathTo,
pathFrom: paths.pathFrom,
x1: zeroW,
x,
y,
goalX: this.barHelpers.getGoalValues(
"x",
zeroW,
/** @type {any} */
null,
i,
j,
0
),
barYPosition,
barHeight
};
}
/** @param {{indexes: any, x: any, y: any, xDivision: any, barWidth: any, zeroH: any, strokeWidth: any, elSeries: any}} opts */
drawColumnPaths({
indexes,
x,
y,
xDivision,
barWidth,
zeroH,
strokeWidth,
elSeries
}) {
const w = this.w;
const realIndex = indexes.realIndex;
const translationsIndex = indexes.translationsIndex;
const i = indexes.i;
const j = indexes.j;
const bc = indexes.bc;
let barXPosition;
if (w.axisFlags.isXNumeric) {
const xForNumericX = this.getBarXForNumericXAxis({
x,
j,
realIndex,
barWidth
});
x = xForNumericX.x;
barXPosition = xForNumericX.barXPosition;
} else {
if (w.config.plotOptions.bar.hideZeroBarsWhenGrouped) {
const { nonZeroColumns, zeroEncounters } = this.barHelpers.getZeroValueEncounters({ i, j });
if (nonZeroColumns > 0) {
barWidth = this.seriesLen * barWidth / nonZeroColumns;
}
barXPosition = x + barWidth * this.visibleI;
barXPosition -= barWidth * zeroEncounters;
} else {
barXPosition = x + barWidth * this.visibleI;
}
}
y = this.barHelpers.getYForValue(
/** @type {any} */
this.series[i][j],
zeroH,
translationsIndex
);
const paths = (
/** @type {any} */
this.barHelpers.getColumnPaths({
barXPosition,
barWidth,
y1: zeroH,
y2: y,
strokeWidth,
isReversed: this.isReversed,
series: this.series,
realIndex,
i,
j,
w
})
);
if (!w.axisFlags.isXNumeric) {
x = x + xDivision;
}
this.barHelpers.barBackground({
bc,
j,
i,
x1: barXPosition - strokeWidth / 2 - barWidth * this.visibleI,
x2: barWidth * this.seriesLen + strokeWidth / 2,
elSeries
});
return {
pathTo: paths.pathTo,
pathFrom: paths.pathFrom,
x,
y,
goalY: this.barHelpers.getGoalValues(
"y",
/** @type {any} */
null,
zeroH,
i,
j,
translationsIndex
),
barXPosition,
barWidth
};
}
/** @param {{x: any, barWidth: any, realIndex: any, j: any}} opts */
getBarXForNumericXAxis({ x, barWidth, realIndex, j }) {
const w = this.w;
let sxI = realIndex;
if (!w.seriesData.seriesX[realIndex].length) {
sxI = w.globals.maxValsInArrayIndex;
}
if (Utils$1.isNumber(w.seriesData.seriesX[sxI][j])) {
x = (w.seriesData.seriesX[sxI][j] - w.globals.minX) / this.xRatio - barWidth * this.seriesLen / 2;
}
return {
barXPosition: x + barWidth * this.visibleI,
x
};
}
/** getPreviousPath is a common function for bars/columns which is used to get previous paths when data changes.
* @memberof Bar
* @param {number} realIndex - current iterating i
* @param {number} j - current iterating series's j index
* @return {string} pathFrom is the string which will be appended in animations
**/
getPreviousPath(realIndex, j) {
const w = this.w;
let pathFrom = "M 0 0";
for (let pp = 0; pp < w.globals.previousPaths.length; pp++) {
const gpp = w.globals.previousPaths[pp];
if (gpp.paths && gpp.paths.length > 0 && parseInt(gpp.realIndex, 10) === parseInt(String(realIndex), 10)) {
if (typeof w.globals.previousPaths[pp].paths[j] !== "undefined") {
pathFrom = w.globals.previousPaths[pp].paths[j].d;
}
}
}
return pathFrom;
}
}
class BarStacked extends Bar {
/**
* @param {any[]} series
* @param {number} seriesIndex
*/
draw(series, seriesIndex) {
const w = this.w;
this.graphics = new Graphics(this.w);
this.bar = new Bar(this.w, this.ctx, this.xyRatios);
const coreUtils = new CoreUtils(this.w);
series = coreUtils.getLogSeries(series);
this.yRatio = coreUtils.getLogYRatios(this.yRatio);
this.barHelpers.initVariables(series);
if (w.config.chart.stackType === "100%") {
series = w.globals.comboCharts ? (
/** @type {any} */
seriesIndex.map(
(_) => w.globals.seriesPercent[_]
)
) : w.globals.seriesPercent.slice();
}
this.series = series;
this.barHelpers.initializeStackedPrevVars(this);
const ret = this.graphics.group({
class: "apexcharts-bar-series apexcharts-plot-series"
});
let x = 0;
let y = 0;
for (let i = 0, bc = 0; i < series.length; i++, bc++) {
const realIndex = w.globals.comboCharts ? (
/** @type {any} */
seriesIndex[i]
) : i;
const { groupIndex, columnGroupIndex } = this.barHelpers.getGroupIndex(realIndex);
this.groupCtx = /** @type {any} */
this[
/** @type {any} */
w.labelData.seriesGroups[groupIndex]
];
const xArrValues = [];
const yArrValues = [];
let translationsIndex = 0;
if (this.yRatio.length > 1) {
this.yaxisIndex = /** @type {any} */
w.globals.seriesYAxisReverseMap[realIndex][0];
translationsIndex = realIndex;
}
this.isReversed = w.config.yaxis[this.yaxisIndex] && w.config.yaxis[this.yaxisIndex].reversed;
let elSeries = this.graphics.group({
class: `apexcharts-series`,
seriesName: Utils$1.escapeString(w.seriesData.seriesNames[realIndex]),
rel: i + 1,
"data:realIndex": realIndex
});
Series.addCollapsedClassToSeries(this.w, elSeries, realIndex);
const elDataLabelsWrap = this.graphics.group({
class: "apexcharts-datalabels",
"data:realIndex": realIndex
});
const elGoalsMarkers = this.graphics.group({
class: "apexcharts-bar-goals-markers"
});
const initPositions = this.initialPositions(
x,
y,
void 0,
void 0,
void 0,
void 0,
translationsIndex
);
const {
xDivision,
// xDivision is the GRIDWIDTH divided by number of datapoints (columns)
yDivision,
// yDivision is the GRIDHEIGHT divided by number of datapoints (bars)
zeroH,
// zeroH is the baseline where 0 meets y axis
zeroW
// zeroW is the baseline where 0 meets x axis
} = initPositions;
let barHeight = initPositions.barHeight;
let barWidth = initPositions.barWidth;
y = initPositions.y;
x = initPositions.x;
w.globals.barHeight = barHeight;
w.globals.barWidth = barWidth;
this.barHelpers.initializeStackedXYVars(this);
if (this.groupCtx.prevY.length === 1 && /**
* @param {number} val
*/
this.groupCtx.prevY[0].every((val) => isNaN(val))) {
this.groupCtx.prevY[0] = this.groupCtx.prevY[0].map(() => zeroH);
this.groupCtx.prevYF[0] = this.groupCtx.prevYF[0].map(() => 0);
}
for (let j = 0; j < w.globals.dataPoints; j++) {
const strokeWidth = this.barHelpers.getStrokeWidth(i, j, realIndex);
const commonPathOpts = {
indexes: { i, j, realIndex, translationsIndex, bc },
strokeWidth,
x,
y,
elSeries,
columnGroupIndex,
seriesGroup: w.labelData.seriesGroups[groupIndex]
};
let paths = (
/** @type {any} */
null
);
if (this.isHorizontal) {
paths = this.drawStackedBarPaths(__spreadProps(__spreadValues({}, commonPathOpts), {
zeroW,
barHeight,
yDivision
}));
barWidth = this.series[i][j] / this.invertedYRatio;
} else {
paths = this.drawStackedColumnPaths(__spreadProps(__spreadValues({}, commonPathOpts), {
xDivision,
barWidth,
zeroH
}));
barHeight = this.series[i][j] / this.yRatio[translationsIndex];
}
const barGoalLine = this.barHelpers.drawGoalLine({
barXPosition: paths.barXPosition,
barYPosition: paths.barYPosition,
goalX: paths.goalX,
goalY: paths.goalY,
barHeight,
barWidth
});
if (barGoalLine) {
elGoalsMarkers.add(barGoalLine);
}
y = paths.y;
x = paths.x;
xArrValues.push(x);
yArrValues.push(y);
const pathFill = this.barHelpers.getPathFillColor(
series,
i,
j,
realIndex
);
let classes = "";
const flipClass = w.globals.isBarHorizontal ? "apexcharts-flip-x" : "apexcharts-flip-y";
if (this.barHelpers.arrBorderRadius[realIndex][j] === "bottom" && w.seriesData.series[realIndex][j] > 0 || this.barHelpers.arrBorderRadius[realIndex][j] === "top" && w.seriesData.series[realIndex][j] < 0) {
classes = flipClass;
}
elSeries = this.renderSeries(__spreadProps(__spreadValues({
realIndex,
pathFill: pathFill.color
}, pathFill.useRangeColor ? { lineFill: pathFill.color } : {}), {
j,
i,
columnGroupIndex,
pathFrom: paths.pathFrom,
pathTo: paths.pathTo,
strokeWidth,
elSeries,
x,
y,
series,
barHeight,
barWidth,
elDataLabelsWrap,
elGoalsMarkers,
type: "bar",
visibleSeries: columnGroupIndex,
classes
}));
}
w.globals.seriesXvalues[realIndex] = xArrValues;
w.globals.seriesYvalues[realIndex] = yArrValues;
this.groupCtx.prevY.push(this.groupCtx.yArrj);
this.groupCtx.prevYF.push(this.groupCtx.yArrjF);
this.groupCtx.prevYVal.push(this.groupCtx.yArrjVal);
this.groupCtx.prevX.push(this.groupCtx.xArrj);
this.groupCtx.prevXF.push(this.groupCtx.xArrjF);
this.groupCtx.prevXVal.push(this.groupCtx.xArrjVal);
ret.add(elSeries);
}
return ret;
}
/**
* @param {number} x
* @param {number} y
* @param {number | undefined} xDivision
* @param {number | undefined} yDivision
* @param {number | undefined} zeroH
* @param {number | undefined} zeroW
* @param {number} translationsIndex
*/
initialPositions(x, y, xDivision, yDivision, zeroH, zeroW, translationsIndex) {
const w = this.w;
let barHeight, barWidth;
if (this.isHorizontal) {
yDivision = w.layout.gridHeight / w.globals.dataPoints;
const userBarHeight = w.config.plotOptions.bar.barHeight;
if (String(userBarHeight).indexOf("%") === -1) {
barHeight = parseInt(userBarHeight, 10);
} else {
barHeight = yDivision * parseInt(userBarHeight, 10) / 100;
}
zeroW = w.globals.padHorizontal + (this.isReversed ? w.layout.gridWidth - this.baseLineInvertedY : this.baseLineInvertedY);
y = (yDivision - barHeight) / 2;
} else {
xDivision = w.layout.gridWidth / w.globals.dataPoints;
barWidth = xDivision;
const userColumnWidth = w.config.plotOptions.bar.columnWidth;
if (w.axisFlags.isXNumeric && w.globals.dataPoints > 1) {
xDivision = w.globals.minXDiff / this.xRatio;
barWidth = xDivision * parseInt(this.barOptions.columnWidth, 10) / 100;
} else if (String(userColumnWidth).indexOf("%") === -1) {
barWidth = parseInt(userColumnWidth, 10);
} else {
barWidth *= parseInt(userColumnWidth, 10) / 100;
}
if (this.isReversed) {
zeroH = this.baseLineY[translationsIndex];
} else {
zeroH = w.layout.gridHeight - this.baseLineY[translationsIndex];
}
x = w.globals.padHorizontal + (xDivision - barWidth) / 2;
}
const subDivisions = w.globals.barGroups.length || 1;
return {
x,
y,
yDivision,
xDivision,
barHeight: (barHeight != null ? barHeight : 0) / subDivisions,
barWidth: (barWidth != null ? barWidth : 0) / subDivisions,
zeroH,
zeroW
};
}
/** @param {{indexes: any, barHeight: any, strokeWidth: any, zeroW: any, x: any, y: any, columnGroupIndex: any, seriesGroup: any, yDivision: any, elSeries: any}} opts */
drawStackedBarPaths({
indexes,
barHeight,
strokeWidth,
zeroW,
x,
y,
columnGroupIndex,
seriesGroup,
yDivision,
elSeries
}) {
var _a, _b, _c, _d, _e;
const w = this.w;
const barYPosition = y + columnGroupIndex * barHeight;
let barXPosition;
const i = indexes.i;
const j = indexes.j;
const realIndex = indexes.realIndex;
const translationsIndex = indexes.translationsIndex;
let prevBarW = 0;
for (let k = 0; k < this.groupCtx.prevXF.length; k++) {
prevBarW = prevBarW + this.groupCtx.prevXF[k][j];
}
let gsi = i;
if (
/** @type {Record<string,any>} */
w.config.series[realIndex].name
) {
gsi = seriesGroup.indexOf(
/** @type {Record<string,any>} */
w.config.series[realIndex].name
);
}
if (gsi > 0) {
let bXP = zeroW;
if (this.groupCtx.prevXVal[gsi - 1][j] < 0) {
bXP = /** @type {any} */
((_a = this.series[i]) == null ? void 0 : _a[j]) >= 0 ? this.groupCtx.prevX[gsi - 1][j] + prevBarW - (this.isReversed ? prevBarW : 0) * 2 : this.groupCtx.prevX[gsi - 1][j];
} else if (this.groupCtx.prevXVal[gsi - 1][j] >= 0) {
bXP = /** @type {any} */
((_b = this.series[i]) == null ? void 0 : _b[j]) >= 0 ? this.groupCtx.prevX[gsi - 1][j] : this.groupCtx.prevX[gsi - 1][j] - prevBarW + (this.isReversed ? prevBarW : 0) * 2;
}
barXPosition = bXP;
} else {
barXPosition = zeroW;
}
if (
/** @type {any} */
((_c = this.series[i]) == null ? void 0 : _c[j]) === null
) {
x = barXPosition;
} else {
x = barXPosition + /** @type {any} */
((_d = this.series[i]) == null ? void 0 : _d[j]) / this.invertedYRatio - (this.isReversed ? (
/** @type {any} */
((_e = this.series[i]) == null ? void 0 : _e[j]) / this.invertedYRatio
) : 0) * 2;
}
const paths = this.barHelpers.getBarpaths({
barYPosition,
barHeight,
x1: barXPosition,
x2: x,
strokeWidth,
isReversed: this.isReversed,
series: this.series,
realIndex: indexes.realIndex,
seriesGroup,
i,
j,
w
});
this.barHelpers.barBackground({
j,
i,
y1: barYPosition,
y2: barHeight,
elSeries
});
y = y + yDivision;
return {
pathTo: paths.pathTo,
pathFrom: paths.pathFrom,
goalX: this.barHelpers.getGoalValues(
"x",
zeroW,
/** @type {any} */
null,
i,
j,
translationsIndex
),
barXPosition,
barYPosition,
x,
y
};
}
/** @param {{indexes: any, x: any, y: any, xDivision: any, barWidth: any, zeroH: any, columnGroupIndex: any, seriesGroup: any, elSeries: any}} opts */
drawStackedColumnPaths({
indexes,
x,
y,
xDivision,
barWidth,
zeroH,
columnGroupIndex,
seriesGroup,
elSeries
}) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
const w = this.w;
const i = indexes.i;
const j = indexes.j;
const bc = indexes.bc;
const realIndex = indexes.realIndex;
const translationsIndex = indexes.translationsIndex;
if (w.axisFlags.isXNumeric) {
let seriesVal = w.seriesData.seriesX[realIndex][j];
if (!seriesVal) seriesVal = 0;
x = (seriesVal - w.globals.minX) / this.xRatio - barWidth / 2 * w.globals.barGroups.length;
}
const barXPosition = x + columnGroupIndex * barWidth;
let barYPosition;
let prevBarH = 0;
for (let k = 0; k < this.groupCtx.prevYF.length; k++) {
prevBarH = prevBarH + (!isNaN(this.groupCtx.prevYF[k][j]) ? this.groupCtx.prevYF[k][j] : 0);
}
let gsi = i;
if (seriesGroup) {
gsi = seriesGroup.indexOf(w.seriesData.seriesNames[realIndex]);
}
if (gsi > 0 && !w.axisFlags.isXNumeric || gsi > 0 && w.axisFlags.isXNumeric && w.seriesData.seriesX[realIndex - 1][j] === w.seriesData.seriesX[realIndex][j]) {
let bYP;
let prevYValue;
const p = Math.min(this.yRatio.length + 1, realIndex + 1);
if (this.groupCtx.prevY[gsi - 1] !== void 0 && this.groupCtx.prevY[gsi - 1].length) {
for (let ii = 1; ii < p; ii++) {
if (!isNaN((_a = this.groupCtx.prevY[gsi - ii]) == null ? void 0 : _a[j])) {
prevYValue = this.groupCtx.prevY[gsi - ii][j];
break;
}
}
}
for (let ii = 1; ii < p; ii++) {
if (((_b = this.groupCtx.prevYVal[gsi - ii]) == null ? void 0 : _b[j]) < 0) {
bYP = /** @type {any} */
((_c = this.series[i]) == null ? void 0 : _c[j]) >= 0 ? prevYValue - prevBarH + (this.isReversed ? prevBarH : 0) * 2 : prevYValue;
break;
} else if (((_d = this.groupCtx.prevYVal[gsi - ii]) == null ? void 0 : _d[j]) >= 0) {
bYP = /** @type {any} */
((_e = this.series[i]) == null ? void 0 : _e[j]) >= 0 ? prevYValue : prevYValue + prevBarH - (this.isReversed ? prevBarH : 0) * 2;
break;
}
}
if (typeof bYP === "undefined") bYP = w.layout.gridHeight;
if (
/**
* @param {number} val
*/
((_f = this.groupCtx.prevYF[0]) == null ? void 0 : _f.every((val) => val === 0)) && this.groupCtx.prevYF.slice(1, gsi).every(
(arr) => arr.every((val) => isNaN(val))
)
) {
barYPosition = zeroH;
} else {
barYPosition = bYP;
}
} else {
barYPosition = zeroH;
}
if (
/** @type {any} */
(_g = this.series[i]) == null ? void 0 : _g[j]
) {
y = barYPosition - /** @type {any} */
((_h = this.series[i]) == null ? void 0 : _h[j]) / this.yRatio[translationsIndex] + (this.isReversed ? (
/** @type {any} */
((_i = this.series[i]) == null ? void 0 : _i[j]) / this.yRatio[translationsIndex]
) : 0) * 2;
} else {
y = barYPosition;
}
const paths = this.barHelpers.getColumnPaths({
barXPosition,
barWidth,
y1: barYPosition,
y2: y,
yRatio: this.yRatio[translationsIndex],
strokeWidth: this.strokeWidth,
isReversed: this.isReversed,
series: this.series,
seriesGroup,
realIndex: indexes.realIndex,
i,
j,
w
});
this.barHelpers.barBackground({
bc,
j,
i,
x1: barXPosition,
x2: barWidth,
elSeries
});
return {
pathTo: paths.pathTo,
pathFrom: paths.pathFrom,
goalY: this.barHelpers.getGoalValues(
"y",
/** @type {any} */
null,
zeroH,
i,
j,
0
),
barXPosition,
x: w.axisFlags.isXNumeric ? x : x + xDivision,
y
};
}
}
class BoxCandleStick extends Bar {
/**
* @param {any[]} series
* @param {string} ctype
* @param {number} seriesIndex
*/
// @ts-ignore -- BoxCandleStick.draw has an extra ctype param compared to Bar.draw
draw(series, ctype, seriesIndex) {
const w = this.w;
const graphics = new Graphics(this.w);
const type = w.globals.comboCharts ? ctype : w.config.chart.type;
const fill = new Fill(this.w);
this.candlestickOptions = this.w.config.plotOptions.candlestick;
this.boxOptions = this.w.config.plotOptions.boxPlot;
this.isHorizontal = w.config.plotOptions.bar.horizontal;
this.isOHLC = this.candlestickOptions && this.candlestickOptions.type === "ohlc";
this.coreUtils = new CoreUtils(this.w);
series = this.coreUtils.getLogSeries(series);
this.series = series;
this.yRatio = this.coreUtils.getLogYRatios(this.yRatio);
this.barHelpers.initVariables(series);
const ret = graphics.group({
class: `apexcharts-${type}-series apexcharts-plot-series`
});
for (let i = 0; i < series.length; i++) {
this.isBoxPlot = w.config.chart.type === "boxPlot" || /** @type {Record<string,any>} */
w.config.series[i].type === "boxPlot";
let x;
let y;
const yArrj = [];
const xArrj = [];
const realIndex = w.globals.comboCharts ? (
/** @type {any} */
seriesIndex[i]
) : i;
const { columnGroupIndex } = this.barHelpers.getGroupIndex(realIndex);
const elSeries = graphics.group({
class: `apexcharts-series`,
seriesName: Utils$1.escapeString(w.seriesData.seriesNames[realIndex]),
rel: i + 1,
"data:realIndex": realIndex
});
Series.addCollapsedClassToSeries(this.w, elSeries, realIndex);
if (series[i].length > 0) {
this.visibleI = this.visibleI + 1;
}
let translationsIndex = 0;
if (this.yRatio.length > 1) {
this.yaxisIndex = /** @type {any} */
w.globals.seriesYAxisReverseMap[realIndex][0];
translationsIndex = realIndex;
}
const initPositions = this.barHelpers.initialPositions(realIndex);
const {
y: initY,
barHeight,
yDivision,
// yDivision is the GRIDHEIGHT divided by number of datapoints (bars)
zeroW,
// zeroW is the baseline where 0 meets x axis
x: initX,
barWidth,
xDivision,
// xDivision is the GRIDWIDTH divided by number of datapoints (columns)
zeroH
// zeroH is the baseline where 0 meets y axis
} = initPositions;
y = initY;
x = initX;
xArrj.push(x + (barWidth != null ? barWidth : 0) / 2);
const elDataLabelsWrap = graphics.group({
class: "apexcharts-datalabels",
"data:realIndex": realIndex
});
const elGoalsMarkers = graphics.group({
class: "apexcharts-bar-goals-markers"
});
for (let j = 0; j < w.globals.dataPoints; j++) {
const strokeWidth = this.barHelpers.getStrokeWidth(i, j, realIndex);
let paths = (
/** @type {any} */
null
);
const pathsParams = {
indexes: {
i,
j,
realIndex,
translationsIndex
},
x,
y,
strokeWidth,
elSeries
};
if (this.isHorizontal) {
paths = this.drawHorizontalBoxPaths(__spreadProps(__spreadValues({}, pathsParams), {
yDivision,
barHeight,
zeroW
}));
} else {
paths = this.drawVerticalBoxPaths(__spreadProps(__spreadValues({}, pathsParams), {
xDivision,
barWidth,
zeroH
}));
}
y = paths.y;
x = paths.x;
const barGoalLine = this.barHelpers.drawGoalLine({
barXPosition: paths.barXPosition,
barYPosition: paths.barYPosition,
goalX: paths.goalX,
goalY: paths.goalY,
barHeight,
barWidth
});
if (barGoalLine) {
elGoalsMarkers.add(barGoalLine);
}
if (j > 0) {
xArrj.push(x + (barWidth != null ? barWidth : 0) / 2);
}
yArrj.push(y);
paths.pathTo.forEach(
(pathTo, pi) => {
const lineFill = !this.isBoxPlot && this.candlestickOptions.wick.useFillColor ? paths.color[pi] : w.globals.stroke.colors[i];
const pathFill = fill.fillPath({
seriesNumber: realIndex,
dataPointIndex: j,
color: paths.color[pi],
value: series[i][j]
});
this.renderSeries({
realIndex,
pathFill,
lineFill,
j,
i,
pathFrom: paths.pathFrom,
pathTo,
strokeWidth,
elSeries,
x,
y,
series,
columnGroupIndex,
barHeight,
barWidth,
elDataLabelsWrap,
elGoalsMarkers,
visibleSeries: this.visibleI,
type: w.config.chart.type
});
}
);
}
w.globals.seriesXvalues[realIndex] = xArrj;
w.globals.seriesYvalues[realIndex] = yArrj;
ret.add(elSeries);
}
return ret;
}
/** @param {{indexes: any, x: any, xDivision: any, barWidth: any, zeroH: any, strokeWidth: any}} opts */
drawVerticalBoxPaths({
indexes,
x,
xDivision,
barWidth,
zeroH,
strokeWidth
}) {
var _a, _b;
const w = this.w;
const graphics = new Graphics(this.w);
const i = indexes.i;
const j = indexes.j;
const { colors: candleColors } = w.config.plotOptions.candlestick;
const { colors: boxColors } = this.boxOptions;
const realIndex = indexes.realIndex;
const getColor = (color2) => Array.isArray(color2) ? color2[realIndex] : color2;
const colorPos = getColor(candleColors.upward);
const colorNeg = getColor(candleColors.downward);
const yRatio = this.yRatio[indexes.translationsIndex];
const ohlc = this.getOHLCValue(realIndex, j);
let l1 = zeroH;
let l2 = zeroH;
let color = ohlc.o < ohlc.c ? [colorPos] : [colorNeg];
if (this.isBoxPlot) {
color = [getColor(boxColors.lower), getColor(boxColors.upper)];
}
let y1 = Math.min(ohlc.o, ohlc.c);
let y2 = Math.max(ohlc.o, ohlc.c);
let m = ohlc.m;
if (w.axisFlags.isXNumeric) {
x = (w.seriesData.seriesX[realIndex][j] - w.globals.minX) / this.xRatio - barWidth / 2;
}
const barXPosition = x + barWidth * this.visibleI;
if (typeof /** @type {any} */
((_a = this.series[i]) == null ? void 0 : _a[j]) === "undefined" || /** @type {any} */
((_b = this.series[i]) == null ? void 0 : _b[j]) === null) {
y1 = zeroH;
y2 = zeroH;
} else {
y1 = zeroH - y1 / yRatio;
y2 = zeroH - y2 / yRatio;
l1 = zeroH - ohlc.h / yRatio;
l2 = zeroH - ohlc.l / yRatio;
m = zeroH - ohlc.m / yRatio;
}
let pathTo;
let pathFrom = graphics.move(barXPosition + barWidth / 2, y1);
if (w.globals.previousPaths.length > 0) {
pathFrom = this.getPreviousPath(realIndex, j);
}
if (this.isOHLC) {
const centerX = barXPosition + barWidth / 2;
const openY = zeroH - ohlc.o / yRatio;
const closeY = zeroH - ohlc.c / yRatio;
pathTo = [
graphics.move(centerX, l1) + graphics.line(centerX, l2) + graphics.move(centerX, openY) + graphics.line(barXPosition, openY) + graphics.move(centerX, closeY) + graphics.line(barXPosition + barWidth, closeY)
];
} else if (this.isBoxPlot) {
pathTo = [
graphics.move(barXPosition, y1) + graphics.line(barXPosition + barWidth / 2, y1) + graphics.line(barXPosition + barWidth / 2, l1) + graphics.line(barXPosition + barWidth / 4, l1) + graphics.line(barXPosition + barWidth - barWidth / 4, l1) + graphics.line(barXPosition + barWidth / 2, l1) + graphics.line(barXPosition + barWidth / 2, y1) + graphics.line(barXPosition + barWidth, y1) + graphics.line(barXPosition + barWidth, m) + graphics.line(barXPosition, m) + graphics.line(barXPosition, y1 + strokeWidth / 2),
graphics.move(barXPosition, m) + graphics.line(barXPosition + barWidth, m) + graphics.line(barXPosition + barWidth, y2) + graphics.line(barXPosition + barWidth / 2, y2) + graphics.line(barXPosition + barWidth / 2, l2) + graphics.line(barXPosition + barWidth - barWidth / 4, l2) + graphics.line(barXPosition + barWidth / 4, l2) + graphics.line(barXPosition + barWidth / 2, l2) + graphics.line(barXPosition + barWidth / 2, y2) + graphics.line(barXPosition, y2) + graphics.line(barXPosition, m) + "z"
];
} else {
pathTo = [
graphics.move(barXPosition, y2) + graphics.line(barXPosition + barWidth / 2, y2) + graphics.line(barXPosition + barWidth / 2, l1) + graphics.line(barXPosition + barWidth / 2, y2) + graphics.line(barXPosition + barWidth, y2) + graphics.line(barXPosition + barWidth, y1) + graphics.line(barXPosition + barWidth / 2, y1) + graphics.line(barXPosition + barWidth / 2, l2) + graphics.line(barXPosition + barWidth / 2, y1) + graphics.line(barXPosition, y1) + graphics.line(barXPosition, y2 - strokeWidth / 2)
];
}
pathFrom = pathFrom + graphics.move(barXPosition, y1);
if (!w.axisFlags.isXNumeric) {
x = x + xDivision;
}
return {
pathTo,
pathFrom,
x,
y: y2,
goalY: this.barHelpers.getGoalValues(
"y",
/** @type {any} */
null,
zeroH,
i,
j,
indexes.translationsIndex
),
barXPosition,
color
};
}
/** @param {{indexes: any, y: any, yDivision: any, barHeight: any, zeroW: any, strokeWidth: any}} opts */
drawHorizontalBoxPaths({
indexes,
y,
yDivision,
barHeight,
zeroW,
strokeWidth
}) {
var _a, _b;
const w = this.w;
const graphics = new Graphics(this.w);
const i = indexes.i;
const j = indexes.j;
const realIndex = indexes.realIndex;
const { colors: candleColors } = w.config.plotOptions.candlestick;
const { colors: boxColors } = this.boxOptions;
const getColor = (color2) => Array.isArray(color2) ? color2[realIndex] : color2;
const yRatio = this.invertedYRatio;
const ohlc = this.getOHLCValue(realIndex, j);
let color = ohlc.o < ohlc.c ? [getColor(candleColors.upward)] : [getColor(candleColors.downward)];
if (this.isBoxPlot) {
color = [getColor(boxColors.lower), getColor(boxColors.upper)];
}
let l1 = zeroW;
let l2 = zeroW;
let x1 = Math.min(ohlc.o, ohlc.c);
let x2 = Math.max(ohlc.o, ohlc.c);
let m = ohlc.m;
if (w.axisFlags.isXNumeric) {
y = (w.seriesData.seriesX[realIndex][j] - w.globals.minX) / this.invertedXRatio - barHeight / 2;
}
const barYPosition = y + barHeight * this.visibleI;
if (typeof /** @type {any} */
((_a = this.series[i]) == null ? void 0 : _a[j]) === "undefined" || /** @type {any} */
((_b = this.series[i]) == null ? void 0 : _b[j]) === null) {
x1 = zeroW;
x2 = zeroW;
} else {
x1 = zeroW + x1 / yRatio;
x2 = zeroW + x2 / yRatio;
l1 = zeroW + ohlc.h / yRatio;
l2 = zeroW + ohlc.l / yRatio;
m = zeroW + ohlc.m / yRatio;
}
let pathFrom = graphics.move(x1, barYPosition + barHeight / 2);
if (w.globals.previousPaths.length > 0) {
pathFrom = this.getPreviousPath(realIndex, j);
}
const pathTo = [
graphics.move(x1, barYPosition) + graphics.line(x1, barYPosition + barHeight / 2) + graphics.line(l1, barYPosition + barHeight / 2) + graphics.line(l1, barYPosition + barHeight / 2 - barHeight / 4) + graphics.line(l1, barYPosition + barHeight / 2 + barHeight / 4) + graphics.line(l1, barYPosition + barHeight / 2) + graphics.line(x1, barYPosition + barHeight / 2) + graphics.line(x1, barYPosition + barHeight) + graphics.line(m, barYPosition + barHeight) + graphics.line(m, barYPosition) + graphics.line(x1 + strokeWidth / 2, barYPosition),
graphics.move(m, barYPosition) + graphics.line(m, barYPosition + barHeight) + graphics.line(x2, barYPosition + barHeight) + graphics.line(x2, barYPosition + barHeight / 2) + graphics.line(l2, barYPosition + barHeight / 2) + graphics.line(l2, barYPosition + barHeight - barHeight / 4) + graphics.line(l2, barYPosition + barHeight / 4) + graphics.line(l2, barYPosition + barHeight / 2) + graphics.line(x2, barYPosition + barHeight / 2) + graphics.line(x2, barYPosition) + graphics.line(m, barYPosition) + "z"
];
pathFrom = pathFrom + graphics.move(x1, barYPosition);
if (!w.axisFlags.isXNumeric) {
y = y + yDivision;
}
return {
pathTo,
pathFrom,
x: x2,
y,
goalX: this.barHelpers.getGoalValues(
"x",
zeroW,
/** @type {any} */
null,
i,
j,
0
),
barYPosition,
color
};
}
/**
* @param {number} i
* @param {number} j
*/
getOHLCValue(i, j) {
const w = this.w;
const coreUtils = this.coreUtils;
const getCandleVal = (arr) => arr[i] && arr[i][j] != null ? (
/** @type {any} */
coreUtils.getLogValAtSeriesIndex(arr[i][j], i)
) : 0;
const h = getCandleVal(w.candleData.seriesCandleH);
const o = getCandleVal(w.candleData.seriesCandleO);
const m = getCandleVal(w.candleData.seriesCandleM);
const c = getCandleVal(w.candleData.seriesCandleC);
const l = getCandleVal(w.candleData.seriesCandleL);
return {
o: this.isBoxPlot ? h : o,
h: this.isBoxPlot ? o : h,
m,
l: this.isBoxPlot ? c : l,
c: this.isBoxPlot ? l : c
};
}
}
class TreemapHelpers {
/**
* @param {import('../../../types/internal').ChartStateW} w
* @param {import('../../../types/internal').ChartContext} ctx
*/
constructor(w, ctx) {
this.ctx = ctx;
this.w = w;
}
checkColorRange() {
const w = this.w;
let negRange = false;
const chartOpts = w.config.plotOptions[w.config.chart.type];
if (chartOpts.colorScale.ranges.length > 0) {
chartOpts.colorScale.ranges.map((range) => {
if (range.from <= 0) {
negRange = true;
}
});
}
return negRange;
}
/**
* @param {string} chartType
* @param {number} i
* @param {number} j
* @param {any} negRange
*/
getShadeColor(chartType, i, j, negRange) {
const w = this.w;
let colorShadePercent = 1;
const shadeIntensity = w.config.plotOptions[chartType].shadeIntensity;
const colorProps = this.determineColor(chartType, i, j);
if (
/** @type {any} */
w.globals.hasNegs || negRange
) {
if (w.config.plotOptions[chartType].reverseNegativeShade) {
if (colorProps.percent < 0) {
colorShadePercent = colorProps.percent / 100 * (shadeIntensity * 1.25);
} else {
colorShadePercent = (1 - colorProps.percent / 100) * (shadeIntensity * 1.25);
}
} else {
if (colorProps.percent <= 0) {
colorShadePercent = 1 - (1 + colorProps.percent / 100) * shadeIntensity;
} else {
colorShadePercent = (1 - colorProps.percent / 100) * shadeIntensity;
}
}
} else {
colorShadePercent = 1 - colorProps.percent / 100;
if (chartType === "treemap") {
colorShadePercent = (1 - colorProps.percent / 100) * (shadeIntensity * 1.25);
}
}
let color = colorProps.color;
const utils = new Utils$1();
if (w.config.plotOptions[chartType].enableShades) {
if (this.w.config.theme.mode === "dark") {
const shadeColor = utils.shadeColor(
colorShadePercent * -1,
colorProps.color
);
color = Utils$1.hexToRgba(
Utils$1.isColorHex(shadeColor) ? shadeColor : Utils$1.rgb2hex(shadeColor),
w.config.fill.opacity
);
} else {
const shadeColor = utils.shadeColor(colorShadePercent, colorProps.color);
color = Utils$1.hexToRgba(
Utils$1.isColorHex(shadeColor) ? shadeColor : Utils$1.rgb2hex(shadeColor),
w.config.fill.opacity
);
}
}
return { color, colorProps };
}
/**
* @param {string} chartType
* @param {number} i
* @param {number} j
*/
determineColor(chartType, i, j) {
const w = this.w;
const val = w.seriesData.series[i][j];
const chartOpts = w.config.plotOptions[chartType];
let seriesNumber = chartOpts.colorScale.inverse ? j : i;
if (chartOpts.distributed && w.config.chart.type === "treemap") {
seriesNumber = j;
}
let color = w.globals.colors[seriesNumber];
let foreColor = null;
let min = Math.min(...w.seriesData.series[i]);
let max = Math.max(...w.seriesData.series[i]);
if (!chartOpts.distributed && chartType === "heatmap") {
min = w.globals.minY;
max = w.globals.maxY;
}
if (typeof chartOpts.colorScale.min !== "undefined") {
min = chartOpts.colorScale.min < w.globals.minY ? chartOpts.colorScale.min : w.globals.minY;
max = chartOpts.colorScale.max > w.globals.maxY ? chartOpts.colorScale.max : w.globals.maxY;
}
const total = Math.abs(max) + Math.abs(min);
let percent = 100 * val / (total === 0 ? total - 1e-6 : total);
if (chartOpts.colorScale.ranges.length > 0) {
const colorRange = chartOpts.colorScale.ranges;
colorRange.map((range) => {
if (val >= range.from && val <= range.to) {
color = range.color;
foreColor = range.foreColor ? range.foreColor : null;
min = range.from;
max = range.to;
const rTotal = Math.abs(max) + Math.abs(min);
percent = 100 * val / (rTotal === 0 ? rTotal - 1e-6 : rTotal);
}
});
}
return {
color,
foreColor,
percent
};
}
/** @param {{ text?: any, x?: any, y?: any, i?: any, j?: any, colorProps?: any, fontSize?: any, series?: any }} opts */
calculateDataLabels({ text, x, y, i, j, colorProps, fontSize }) {
const w = this.w;
const dataLabelsConfig = w.config.dataLabels;
const graphics = new Graphics(this.w);
const dataLabels = new DataLabels(this.w, this.ctx);
let elDataLabelsWrap = null;
if (dataLabelsConfig.enabled) {
elDataLabelsWrap = graphics.group({
class: "apexcharts-data-labels"
});
const offX = dataLabelsConfig.offsetX;
const offY = dataLabelsConfig.offsetY;
const dataLabelsX = x + offX;
const dataLabelsY = y + parseFloat(dataLabelsConfig.style.fontSize) / 3 + offY;
dataLabels.plotDataLabelsText({
x: dataLabelsX,
y: dataLabelsY,
text,
i,
j,
color: colorProps.foreColor,
parent: elDataLabelsWrap,
fontSize,
dataLabelsConfig
});
}
return elDataLabelsWrap;
}
}
class HeatMap {
/**
* @param {import('../types/internal').ChartStateW} w
* @param {import('../types/internal').ChartContext} ctx
* @param {import('../types/internal').XYRatios} xyRatios
*/
constructor(w, ctx, xyRatios) {
this.ctx = ctx;
this.w = w;
this.xRatio = xyRatios.xRatio;
this.yRatio = xyRatios.yRatio;
this.dynamicAnim = this.w.config.chart.animations.dynamicAnimation;
this.helpers = new TreemapHelpers(w, ctx);
this.rectRadius = this.w.config.plotOptions.heatmap.radius;
this.strokeWidth = this.w.config.stroke.show ? this.w.config.stroke.width : 0;
}
/**
* @param {any[]} series
*/
draw(series) {
const w = this.w;
const graphics = new Graphics(this.w, this.ctx);
const ret = graphics.group({
class: "apexcharts-heatmap"
});
ret.attr("clip-path", `url(#gridRectMask${w.globals.cuid})`);
const xDivision = w.layout.gridWidth / w.globals.dataPoints;
const yDivision = w.layout.gridHeight / w.seriesData.series.length;
let y1 = 0;
let rev = false;
this.negRange = this.helpers.checkColorRange();
const heatSeries = series.slice();
if (w.config.yaxis[0].reversed) {
rev = true;
heatSeries.reverse();
}
for (let i = rev ? 0 : heatSeries.length - 1; rev ? i < heatSeries.length : i >= 0; rev ? i++ : i--) {
const elSeries = graphics.group({
class: `apexcharts-series apexcharts-heatmap-series`,
seriesName: Utils$1.escapeString(w.seriesData.seriesNames[i]),
rel: i + 1,
"data:realIndex": i
});
Series.addCollapsedClassToSeries(this.w, elSeries, i);
graphics.setupEventDelegation(elSeries, ".apexcharts-heatmap-rect");
if (w.config.chart.dropShadow.enabled) {
const shadow = w.config.chart.dropShadow;
const filters = new Filters(this.w);
filters.dropShadow(elSeries, shadow, i);
}
let x1 = 0;
const shadeIntensity = w.config.plotOptions.heatmap.shadeIntensity;
let j = 0;
for (let dIndex = 0; dIndex < w.globals.dataPoints; dIndex++) {
if (w.seriesData.seriesX.length && !w.globals.allSeriesHasEqualX) {
if (w.globals.minX + w.globals.minXDiff * dIndex < w.seriesData.seriesX[i][j]) {
x1 = x1 + xDivision;
continue;
}
}
if (j >= heatSeries[i].length) break;
const heatColor = this.helpers.getShadeColor(
w.config.chart.type,
i,
j,
this.negRange
);
let color = heatColor.color;
const heatColorProps = heatColor.colorProps;
if (w.config.fill.type === "image") {
const fill = new Fill(this.w);
color = fill.fillPath({
seriesNumber: i,
dataPointIndex: j,
opacity: (
/** @type {any} */
w.globals.hasNegs ? heatColorProps.percent < 0 ? 1 - (1 + heatColorProps.percent / 100) : shadeIntensity + heatColorProps.percent / 100 : heatColorProps.percent / 100
),
patternID: Utils$1.randomId(),
width: w.config.fill.image.width ? w.config.fill.image.width : xDivision,
height: w.config.fill.image.height ? w.config.fill.image.height : yDivision
});
}
const radius = this.rectRadius;
const rect = graphics.drawRect(x1, y1, xDivision, yDivision, radius);
rect.attr({
cx: x1,
cy: y1
});
rect.node.classList.add("apexcharts-heatmap-rect");
elSeries.add(rect);
rect.attr({
fill: color,
i,
index: i,
j,
val: series[i][j],
"stroke-width": this.strokeWidth,
stroke: w.config.plotOptions.heatmap.useFillColorAsStroke ? color : w.globals.stroke.colors[0],
color
});
if (w.config.chart.animations.enabled && !w.globals.dataChanged) {
let speed = 1;
if (!w.globals.resized) {
speed = w.config.chart.animations.speed;
}
this.animateHeatMap(rect, x1, y1, xDivision, yDivision, speed);
}
if (w.globals.dataChanged) {
let speed = 1;
if (this.dynamicAnim.enabled && w.globals.shouldAnimate) {
speed = this.dynamicAnim.speed;
let colorFrom = w.globals.previousPaths[i] && w.globals.previousPaths[i][j] && w.globals.previousPaths[i][j].color;
if (!colorFrom) colorFrom = "rgba(255, 255, 255, 0)";
this.animateHeatColor(
rect,
Utils$1.isColorHex(colorFrom) ? colorFrom : Utils$1.rgb2hex(colorFrom),
Utils$1.isColorHex(color) ? color : Utils$1.rgb2hex(color),
speed
);
}
}
const formatter = w.config.dataLabels.formatter;
const formattedText = formatter(w.seriesData.series[i][j], {
value: w.seriesData.series[i][j],
seriesIndex: i,
dataPointIndex: j,
w
});
const dataLabels = this.helpers.calculateDataLabels({
text: formattedText,
x: x1 + xDivision / 2,
y: y1 + yDivision / 2,
i,
j,
colorProps: heatColorProps,
series: heatSeries
});
if (dataLabels !== null) {
elSeries.add(dataLabels);
}
x1 = x1 + xDivision;
j++;
}
y1 = y1 + yDivision;
ret.add(elSeries);
}
const yAxisScale = (
/** @type {any[]} */
w.globals.yAxisScale[0].result.slice()
);
if (w.config.yaxis[0].reversed) {
yAxisScale.unshift("");
} else {
yAxisScale.push("");
}
w.globals.yAxisScale[0].result = yAxisScale;
return ret;
}
/**
* @param {any} el
* @param {number} x
* @param {number} y
* @param {number} width
* @param {number} height
* @param {number} speed
*/
animateHeatMap(el, x, y, width, height, speed) {
const animations = new Animations(this.w);
animations.animateRect(
el,
{
x: x + width / 2,
y: y + height / 2,
width: 0,
height: 0
},
{
x,
y,
width,
height
},
speed,
() => {
animations.animationCompleted(el);
}
);
}
/**
* @param {any} el
* @param {string} colorFrom
* @param {string} colorTo
* @param {number} speed
*/
animateHeatColor(el, colorFrom, colorTo, speed) {
el.attr({
fill: colorFrom
}).animate(speed).attr({
fill: colorTo
});
}
}
class Helpers5 {
/**
* @param {import('../../../charts/Line').default} lineCtx
*/
constructor(lineCtx) {
this.w = lineCtx.w;
this.lineCtx = lineCtx;
}
/**
* @param {number} i
* @param {any[]} series
*/
sameValueSeriesFix(i, series) {
const w = this.w;
if (w.config.fill.type === "gradient" || w.config.fill.type[i] === "gradient") {
const coreUtils = new CoreUtils(this.lineCtx.w);
if (coreUtils.seriesHaveSameValues(i)) {
const gSeries = series[i].slice();
gSeries[gSeries.length - 1] = gSeries[gSeries.length - 1] + 1e-6;
series[i] = gSeries;
}
}
return series;
}
/** @param {{series: any, realIndex: any, x: any, y: any, i: any, j: any, prevY: any}} opts */
calculatePoints({ series, realIndex, x, y, i, j, prevY }) {
const w = this.w;
const ptX = [];
const ptY = [];
let xPT1st = this.lineCtx.categoryAxisCorrection + w.config.markers.offsetX;
if (w.axisFlags.isXNumeric) {
xPT1st = (w.seriesData.seriesX[realIndex][0] - w.globals.minX) / this.lineCtx.xRatio + w.config.markers.offsetX;
}
if (j === 0) {
ptX.push(xPT1st);
ptY.push(
Utils$1.isNumber(series[i][0]) ? prevY + w.config.markers.offsetY : null
);
}
ptX.push(x + w.config.markers.offsetX);
ptY.push(
Utils$1.isNumber(series[i][j + 1]) ? y + w.config.markers.offsetY : null
);
return {
x: ptX,
y: ptY
};
}
/** @param {{pathFromLine: any, pathFromArea: any, realIndex: any}} opts */
checkPreviousPaths({ pathFromLine, pathFromArea, realIndex }) {
const w = this.w;
for (let pp = 0; pp < w.globals.previousPaths.length; pp++) {
const gpp = w.globals.previousPaths[pp];
if ((gpp.type === "line" || gpp.type === "area") && gpp.paths.length > 0 && parseInt(gpp.realIndex, 10) === parseInt(realIndex, 10)) {
if (gpp.type === "line") {
this.lineCtx.appendPathFrom = false;
pathFromLine = w.globals.previousPaths[pp].paths[0].d;
} else if (gpp.type === "area") {
this.lineCtx.appendPathFrom = false;
pathFromArea = w.globals.previousPaths[pp].paths[0].d;
if (w.config.stroke.show && w.globals.previousPaths[pp].paths[1]) {
pathFromLine = w.globals.previousPaths[pp].paths[1].d;
}
}
}
}
return {
pathFromLine,
pathFromArea
};
}
/** @param {{i: any, realIndex: any, series: any, prevY: any, lineYPosition: any, translationsIndex: any}} opts */
determineFirstPrevY({
i,
realIndex,
series,
prevY,
lineYPosition,
translationsIndex
}) {
var _a, _b, _c;
const w = this.w;
const stackSeries = w.config.chart.stacked && !w.globals.comboCharts || w.config.chart.stacked && w.globals.comboCharts && (!this.w.config.chart.stackOnlyBar || /** @type {any} */
((_a = this.w.config.series[realIndex]) == null ? void 0 : _a.type) === "bar" || /** @type {any} */
((_b = this.w.config.series[realIndex]) == null ? void 0 : _b.type) === "column");
if (typeof ((_c = series[i]) == null ? void 0 : _c[0]) !== "undefined") {
if (stackSeries) {
if (i > 0) {
lineYPosition = this.lineCtx.prevSeriesY[i - 1][0];
} else {
lineYPosition = this.lineCtx.zeroY;
}
} else {
lineYPosition = this.lineCtx.zeroY;
}
prevY = lineYPosition - series[i][0] / this.lineCtx.yRatio[translationsIndex] + (this.lineCtx.isReversed ? series[i][0] / this.lineCtx.yRatio[translationsIndex] : 0) * 2;
} else {
if (stackSeries && i > 0 && typeof series[i][0] === "undefined") {
for (let s = i - 1; s >= 0; s--) {
if (series[s][0] !== null && typeof series[s][0] !== "undefined") {
lineYPosition = this.lineCtx.prevSeriesY[s][0];
prevY = lineYPosition;
break;
}
}
}
}
return {
prevY,
lineYPosition
};
}
}
const tangents = (points) => {
const m = finiteDifferences(points);
const n = points.length - 1;
const ε = 1e-6;
const tgts = [];
let a, b, d, s;
for (let i = 0; i < n; i++) {
d = slope(points[i], points[i + 1]);
if (Math.abs(d) < ε) {
m[i] = m[i + 1] = 0;
} else {
a = m[i] / d;
b = m[i + 1] / d;
s = a * a + b * b;
if (s > 9) {
s = d * 3 / Math.sqrt(s);
m[i] = s * a;
m[i + 1] = s * b;
}
}
}
for (let i = 0; i <= n; i++) {
s = (points[Math.min(n, i + 1)][0] - points[Math.max(0, i - 1)][0]) / (6 * (1 + m[i] * m[i]));
tgts.push([s || 0, m[i] * s || 0]);
}
return tgts;
};
const svgPath = (points) => {
let p = "";
for (let i = 0; i < points.length; i++) {
const point = points[i];
const n = point.length;
if (n > 4) {
p += `C${point[0]}, ${point[1]}`;
p += `, ${point[2]}, ${point[3]}`;
p += `, ${point[4]}, ${point[5]}`;
} else if (n > 2) {
p += `S${point[0]}, ${point[1]}`;
p += `, ${point[2]}, ${point[3]}`;
}
}
return p;
};
const spline = {
/**
* Convert 'points' to bezier
* @param {any[]} points
* @returns {any[]}
*/
points(points) {
const tgts = tangents(points);
const p = points[1];
const p0 = points[0];
const pts = [];
const t = tgts[1];
const t0 = tgts[0];
pts.push(p0, [
p0[0] + t0[0],
p0[1] + t0[1],
p[0] - t[0],
p[1] - t[1],
p[0],
p[1]
]);
for (let i = 2, n = tgts.length; i < n; i++) {
const p2 = points[i];
const t2 = tgts[i];
pts.push([p2[0] - t2[0], p2[1] - t2[1], p2[0], p2[1]]);
}
return pts;
},
/**
* Slice out a segment of 'points'
* @param {any[]} points
* @param {Number} start
* @param {Number} end
* @returns {any[]}
*/
slice(points, start, end) {
const pts = points.slice(start, end);
if (start) {
if (end - start > 1 && pts[1].length < 6) {
const n = pts[0].length;
pts[1] = [
pts[0][n - 2] * 2 - pts[0][n - 4],
pts[0][n - 1] * 2 - pts[0][n - 3]
].concat(pts[1]);
}
pts[0] = pts[0].slice(-2);
}
return pts;
}
};
function slope(p0, p1) {
return (p1[1] - p0[1]) / (p1[0] - p0[0]);
}
function finiteDifferences(points) {
const m = [];
let p0 = points[0];
let p1 = points[1];
let d = m[0] = slope(p0, p1);
let i = 1;
for (let n = points.length - 1; i < n; i++) {
p0 = p1;
p1 = points[i + 1];
m[i] = (d + (d = slope(p0, p1))) * 0.5;
}
m[i] = d;
return m;
}
class Line {
/**
* @param {import('../types/internal').ChartStateW} w
* @param {import('../types/internal').ChartContext} ctx
* @param {import('../types/internal').XYRatios} xyRatios
* @param {boolean} isPointsChart
*/
constructor(w, ctx, xyRatios, isPointsChart) {
this.ctx = ctx;
this.w = w;
this.xyRatios = xyRatios;
this.xRatio = 0;
this.yRatio = [];
this.zRatio = 0;
this.baseLineY = [];
this.pointsChart = !(this.w.config.chart.type !== "bubble" && this.w.config.chart.type !== "scatter") || isPointsChart;
this.scatter = new Scatter(this.w, this.ctx);
this.noNegatives = this.w.globals.minX === Number.MAX_VALUE;
this.lineHelpers = new Helpers5(this);
this.markers = new Markers(this.w, this.ctx);
this.prevSeriesY = [];
this.categoryAxisCorrection = 0;
this.yaxisIndex = 0;
this.xDivision = 0;
this.zeroY = 0;
this.areaBottomY = 0;
this.strokeWidth = 0;
this.isReversed = false;
this.appendPathFrom = false;
this.elSeries = null;
this.elPointsMain = null;
this.elDataLabelsWrap = null;
}
/**
* @param {any[]} series
* @param {string} ctype
* @param {number} seriesIndex
* @param {any} seriesRangeEnd
*/
draw(series, ctype, seriesIndex, seriesRangeEnd) {
var _a;
const w = this.w;
const graphics = new Graphics(this.w);
const type = w.globals.comboCharts ? ctype : w.config.chart.type;
const ret = graphics.group({
class: `apexcharts-${type}-series apexcharts-plot-series`
});
const coreUtils = new CoreUtils(this.w);
this.yRatio = this.xyRatios.yRatio;
this.zRatio = this.xyRatios.zRatio;
this.xRatio = this.xyRatios.xRatio;
this.baseLineY = this.xyRatios.baseLineY;
series = coreUtils.getLogSeries(series);
this.yRatio = coreUtils.getLogYRatios(this.yRatio);
this.prevSeriesY = [];
const allSeries = [];
for (let i = 0; i < series.length; i++) {
series = this.lineHelpers.sameValueSeriesFix(i, series);
const realIndex = w.globals.comboCharts ? (
/** @type {any} */
seriesIndex[i]
) : i;
const translationsIndex = this.yRatio.length > 1 ? realIndex : 0;
this._initSerieVariables(series, i, realIndex);
const yArrj = [];
const y2Arrj = [];
const xArrj = [];
let x = w.globals.padHorizontal + this.categoryAxisCorrection;
const y = 1;
const linePaths = [];
const areaPaths = [];
Series.addCollapsedClassToSeries(this.w, this.elSeries, realIndex);
if (w.axisFlags.isXNumeric && w.seriesData.seriesX.length > 0) {
x = (w.seriesData.seriesX[realIndex][0] - w.globals.minX) / this.xRatio;
}
xArrj.push(x);
const pX = x;
let pY2;
const prevX = pX;
let prevY = this.zeroY;
let prevY2 = this.zeroY;
const lineYPosition = 0;
const firstPrevY = this.lineHelpers.determineFirstPrevY({
i,
realIndex,
series,
prevY,
lineYPosition,
translationsIndex
});
prevY = firstPrevY.prevY;
if (w.config.stroke.curve === "monotoneCubic" && series[i][0] === null) {
yArrj.push(null);
} else {
yArrj.push(prevY);
}
const pY = prevY;
let firstPrevY2;
if (type === "rangeArea") {
firstPrevY2 = this.lineHelpers.determineFirstPrevY({
i,
realIndex,
series: seriesRangeEnd,
prevY: prevY2,
lineYPosition,
translationsIndex
});
prevY2 = firstPrevY2.prevY;
pY2 = prevY2;
y2Arrj.push(yArrj[0] !== null ? prevY2 : null);
}
const pathsFrom = this._calculatePathsFrom({
type,
series,
i,
realIndex,
translationsIndex,
prevX,
prevY,
prevY2
});
const rYArrj = [yArrj[0]];
const rY2Arrj = [y2Arrj[0]];
const iteratingOpts = {
type,
series,
realIndex,
translationsIndex,
i,
x,
y,
pX,
pY,
pathsFrom,
linePaths,
areaPaths,
seriesIndex,
lineYPosition,
xArrj,
yArrj,
y2Arrj,
seriesRangeEnd
};
const paths = this._iterateOverDataPoints(__spreadProps(__spreadValues({}, iteratingOpts), {
iterations: type === "rangeArea" ? series[i].length - 1 : void 0,
isRangeStart: true
}));
if (type === "rangeArea") {
const pathsFrom2 = this._calculatePathsFrom({
series: seriesRangeEnd,
i,
realIndex,
prevX,
prevY: prevY2
});
const rangePaths = this._iterateOverDataPoints(__spreadProps(__spreadValues({}, iteratingOpts), {
series: seriesRangeEnd,
xArrj: [x],
yArrj: rYArrj,
y2Arrj: rY2Arrj,
pY: pY2,
areaPaths: paths.areaPaths,
pathsFrom: pathsFrom2,
iterations: seriesRangeEnd[i].length - 1,
isRangeStart: false
}));
const segments = paths.linePaths.length / 2;
for (let s = 0; s < segments; s++) {
paths.linePaths[s] = rangePaths.linePaths[s + segments] + paths.linePaths[s];
}
paths.linePaths.splice(segments);
paths.pathFromLine = rangePaths.pathFromLine + paths.pathFromLine;
} else {
paths.pathFromArea += "z";
}
this._handlePaths({ type, realIndex, i, paths });
this.elSeries.add(this.elPointsMain);
this.elSeries.add(this.elDataLabelsWrap);
allSeries.push(this.elSeries);
}
if (typeof /** @type {Record<string,any>} */
((_a = w.config.series[0]) == null ? void 0 : _a.zIndex) !== "undefined") {
allSeries.sort(
(a, b) => Number(a.node.getAttribute("zIndex")) - Number(b.node.getAttribute("zIndex"))
);
}
if (w.config.chart.stacked) {
for (let s = allSeries.length - 1; s >= 0; s--) {
ret.add(allSeries[s]);
}
} else {
for (let s = 0; s < allSeries.length; s++) {
ret.add(allSeries[s]);
}
}
return ret;
}
/**
* @param {any[]} series
* @param {number} i
* @param {number} realIndex
*/
_initSerieVariables(series, i, realIndex) {
const w = this.w;
const graphics = new Graphics(this.w);
this.xDivision = w.layout.gridWidth / (w.globals.dataPoints - (w.config.xaxis.tickPlacement === "on" ? 1 : 0));
this.strokeWidth = Array.isArray(w.config.stroke.width) ? w.config.stroke.width[realIndex] : w.config.stroke.width;
let translationsIndex = 0;
if (this.yRatio.length > 1) {
this.yaxisIndex = w.globals.seriesYAxisReverseMap[realIndex];
translationsIndex = realIndex;
}
this.isReversed = w.config.yaxis[this.yaxisIndex] && w.config.yaxis[this.yaxisIndex].reversed;
this.zeroY = w.layout.gridHeight - this.baseLineY[translationsIndex] - (this.isReversed ? w.layout.gridHeight : 0) + (this.isReversed ? this.baseLineY[translationsIndex] * 2 : 0);
this.areaBottomY = this.zeroY;
if (this.zeroY > w.layout.gridHeight || w.config.plotOptions.area.fillTo === "end") {
this.areaBottomY = w.layout.gridHeight;
}
this.categoryAxisCorrection = this.xDivision / 2;
const seriesItem = (
/** @type {Record<string,any>} */
w.config.series[realIndex]
);
this.elSeries = graphics.group({
class: `apexcharts-series`,
zIndex: typeof seriesItem.zIndex !== "undefined" ? seriesItem.zIndex : realIndex,
seriesName: Utils$1.escapeString(w.seriesData.seriesNames[realIndex])
});
this.elPointsMain = graphics.group({
class: "apexcharts-series-markers-wrap",
"data:realIndex": realIndex
});
if (w.globals.hasNullValues) {
const firstPoint = this.markers.plotChartMarkers({
pointsPos: {
x: [0],
y: [w.layout.gridHeight + w.globals.markers.largestSize]
},
seriesIndex: i,
j: 0,
pSize: 0.1,
alwaysDrawMarker: true,
isVirtualPoint: true
});
if (firstPoint !== null) {
this.elPointsMain.add(firstPoint);
}
}
this.elDataLabelsWrap = graphics.group({
class: "apexcharts-datalabels",
"data:realIndex": realIndex
});
const longestSeries = series[i].length === w.globals.dataPoints;
this.elSeries.attr({
"data:longestSeries": longestSeries,
rel: i + 1,
"data:realIndex": realIndex
});
this.appendPathFrom = true;
}
/** @param {{ type?: any, series?: any, i?: any, realIndex?: any, translationsIndex?: any, prevX?: any, prevY?: any, prevY2?: any }} opts */
_calculatePathsFrom({
type,
series,
i,
realIndex,
translationsIndex,
prevX,
prevY,
prevY2
}) {
const w = this.w;
const graphics = new Graphics(this.w);
let linePath, areaPath, pathFromLine, pathFromArea;
if (series[i][0] === null) {
for (let s = 0; s < series[i].length; s++) {
if (series[i][s] !== null) {
prevX = this.xDivision * s;
prevY = this.zeroY - series[i][s] / this.yRatio[translationsIndex];
linePath = graphics.move(prevX, prevY);
areaPath = graphics.move(prevX, this.areaBottomY);
break;
}
}
} else {
linePath = graphics.move(prevX, prevY);
if (type === "rangeArea") {
linePath = graphics.move(prevX, prevY2) + graphics.line(prevX, prevY);
}
areaPath = graphics.move(prevX, this.areaBottomY) + graphics.line(prevX, prevY);
}
pathFromLine = graphics.move(0, this.areaBottomY) + graphics.line(0, this.areaBottomY);
pathFromArea = graphics.move(0, this.areaBottomY) + graphics.line(0, this.areaBottomY);
if (w.globals.previousPaths.length > 0) {
const pathFrom = this.lineHelpers.checkPreviousPaths({
pathFromLine,
pathFromArea,
realIndex
});
pathFromLine = pathFrom.pathFromLine;
pathFromArea = pathFrom.pathFromArea;
}
return {
prevX,
prevY,
linePath,
areaPath,
pathFromLine,
pathFromArea
};
}
/** @param {{type: any, realIndex: any, i: any, paths: any}} opts */
_handlePaths({ type, realIndex, i, paths }) {
const w = this.w;
const graphics = new Graphics(this.w);
const fill = new Fill(this.w);
this.prevSeriesY.push(paths.yArrj);
w.globals.seriesXvalues[realIndex] = paths.xArrj;
w.globals.seriesYvalues[realIndex] = paths.yArrj;
const forecast = w.config.forecastDataPoints;
if (forecast.count > 0 && type !== "rangeArea") {
const forecastCutoff = w.globals.seriesXvalues[realIndex][w.globals.seriesXvalues[realIndex].length - forecast.count - 1];
const elForecastMask = graphics.drawRect(
forecastCutoff,
0,
w.layout.gridWidth,
w.layout.gridHeight,
0
);
w.dom.elForecastMask.appendChild(elForecastMask.node);
const elNonForecastMask = graphics.drawRect(
0,
0,
forecastCutoff,
w.layout.gridHeight,
0
);
w.dom.elNonForecastMask.appendChild(elNonForecastMask.node);
}
if (!this.pointsChart) {
w.globals.delayedElements.push({
el: this.elPointsMain.node,
index: realIndex
});
}
const defaultRenderedPathOptions = {
i,
realIndex,
animationDelay: i,
initialSpeed: w.config.chart.animations.speed,
dataChangeSpeed: w.config.chart.animations.dynamicAnimation.speed,
className: `apexcharts-${type}`
};
if (type === "area") {
const pathFill = fill.fillPath({
seriesNumber: realIndex
});
for (let p = 0; p < paths.areaPaths.length; p++) {
const renderedPath = graphics.renderPaths(__spreadProps(__spreadValues({}, defaultRenderedPathOptions), {
pathFrom: paths.pathFromArea,
pathTo: paths.areaPaths[p],
stroke: "none",
strokeWidth: 0,
strokeLineCap: null,
fill: pathFill
}));
this.elSeries.add(renderedPath);
}
}
if (w.config.stroke.show && !this.pointsChart) {
let lineFill = null;
if (type === "line") {
lineFill = fill.fillPath({
seriesNumber: realIndex,
i
});
} else {
if (w.config.stroke.fill.type === "solid") {
lineFill = w.globals.stroke.colors[realIndex];
} else {
const prevFill = w.config.fill;
w.config.fill = w.config.stroke.fill;
lineFill = fill.fillPath({
seriesNumber: realIndex,
i
});
w.config.fill = prevFill;
}
}
for (let p = 0; p < paths.linePaths.length; p++) {
let pathFill = lineFill;
if (type === "rangeArea") {
pathFill = fill.fillPath({
seriesNumber: realIndex
});
}
const linePathCommonOpts = __spreadProps(__spreadValues({}, defaultRenderedPathOptions), {
pathFrom: paths.pathFromLine,
pathTo: paths.linePaths[p],
stroke: lineFill,
strokeWidth: this.strokeWidth,
strokeLineCap: w.config.stroke.lineCap,
fill: type === "rangeArea" ? pathFill : "none"
});
const renderedPath = graphics.renderPaths(linePathCommonOpts);
this.elSeries.add(renderedPath);
renderedPath.attr("fill-rule", `evenodd`);
if (forecast.count > 0 && type !== "rangeArea") {
const renderedForecastPath = graphics.renderPaths(linePathCommonOpts);
renderedForecastPath.node.setAttribute(
"stroke-dasharray",
forecast.dashArray
);
if (forecast.strokeWidth) {
renderedForecastPath.node.setAttribute(
"stroke-width",
forecast.strokeWidth
);
}
this.elSeries.add(renderedForecastPath);
renderedForecastPath.attr(
"clip-path",
`url(#forecastMask${w.globals.cuid})`
);
renderedPath.attr(
"clip-path",
`url(#nonForecastMask${w.globals.cuid})`
);
}
}
}
}
_iterateOverDataPoints({
type,
series,
iterations,
realIndex,
translationsIndex,
i,
x,
y,
pX,
pY,
pathsFrom,
linePaths,
areaPaths,
seriesIndex,
lineYPosition,
xArrj,
yArrj,
y2Arrj,
isRangeStart,
seriesRangeEnd
}) {
var _a, _b;
const w = this.w;
const graphics = new Graphics(this.w);
const yRatio = this.yRatio;
let { prevY, linePath, areaPath, pathFromLine, pathFromArea } = pathsFrom;
const minY = Utils$1.isNumber(w.globals.minYArr[realIndex]) ? w.globals.minYArr[realIndex] : w.globals.minY;
if (!iterations) {
iterations = w.globals.dataPoints > 1 ? w.globals.dataPoints - 1 : w.globals.dataPoints;
}
const getY = (_y, lineYPos) => {
return lineYPos - _y / yRatio[translationsIndex] + (this.isReversed ? _y / yRatio[translationsIndex] : 0) * 2;
};
let y2 = y;
const stackSeries = w.config.chart.stacked && !w.globals.comboCharts || w.config.chart.stacked && w.globals.comboCharts && (!this.w.config.chart.stackOnlyBar || /** @type {Record<string,any>} */
((_a = this.w.config.series[realIndex]) == null ? void 0 : _a.type) === "bar" || /** @type {Record<string,any>} */
((_b = this.w.config.series[realIndex]) == null ? void 0 : _b.type) === "column");
let curve = w.config.stroke.curve;
if (Array.isArray(curve)) {
if (Array.isArray(seriesIndex)) {
curve = curve[seriesIndex[i]];
} else {
curve = curve[i];
}
}
let pathState = 0;
let segmentStartX;
for (let j = 0; j < iterations; j++) {
if (series[i].length === 0) break;
const isNull = typeof series[i][j + 1] === "undefined" || series[i][j + 1] === null;
if (w.axisFlags.isXNumeric) {
let sX = w.seriesData.seriesX[realIndex][j + 1];
if (typeof w.seriesData.seriesX[realIndex][j + 1] === "undefined") {
sX = w.seriesData.seriesX[realIndex][iterations - 1];
}
x = (sX - w.globals.minX) / this.xRatio;
} else {
x = x + this.xDivision;
}
if (stackSeries) {
if (i > 0 && w.globals.collapsedSeries.length < w.config.series.length - 1) {
const prevIndex = (pi) => {
for (let pii = pi; pii > 0; pii--) {
if (w.globals.collapsedSeriesIndices.indexOf(
(seriesIndex == null ? void 0 : seriesIndex[pii]) || pii
) > -1) {
pii--;
} else {
return pii;
}
}
return 0;
};
lineYPosition = this.prevSeriesY[prevIndex(i - 1)][j + 1];
} else {
lineYPosition = this.zeroY;
}
} else {
lineYPosition = this.zeroY;
}
if (isNull) {
y = getY(minY, lineYPosition);
} else {
y = getY(series[i][j + 1], lineYPosition);
if (type === "rangeArea") {
y2 = getY(seriesRangeEnd[i][j + 1], lineYPosition);
}
}
xArrj.push(series[i][j + 1] === null ? null : x);
if (isNull && (w.config.stroke.curve === "smooth" || w.config.stroke.curve === "monotoneCubic")) {
yArrj.push(null);
y2Arrj.push(null);
} else {
yArrj.push(y);
y2Arrj.push(y2);
}
const pointsPos = this.lineHelpers.calculatePoints({
series,
x,
y,
realIndex,
i,
j,
prevY
});
const calculatedPaths = this._createPaths({
type,
series,
i,
j,
x,
y,
y2,
xArrj,
yArrj,
y2Arrj,
pX,
pY,
pathState,
segmentStartX,
linePath,
areaPath,
linePaths,
areaPaths,
curve,
isRangeStart
});
areaPaths = calculatedPaths.areaPaths;
linePaths = calculatedPaths.linePaths;
pX = calculatedPaths.pX;
pY = calculatedPaths.pY;
pathState = calculatedPaths.pathState;
segmentStartX = calculatedPaths.segmentStartX;
areaPath = calculatedPaths.areaPath;
linePath = calculatedPaths.linePath;
if (this.appendPathFrom && !w.globals.hasNullValues && !(curve === "monotoneCubic" && type === "rangeArea")) {
pathFromLine += graphics.line(x, this.areaBottomY);
pathFromArea += graphics.line(x, this.areaBottomY);
}
this.handleNullDataPoints(series, pointsPos, i, j, realIndex);
this._handleMarkersAndLabels({
type,
pointsPos,
i,
j,
realIndex,
isRangeStart
});
}
return {
yArrj,
xArrj,
pathFromArea,
areaPaths,
pathFromLine,
linePaths,
linePath,
areaPath
};
}
/** @param {{type: any, pointsPos: any, isRangeStart: any, i: any, j: any, realIndex: any}} opts */
_handleMarkersAndLabels({ type, pointsPos, isRangeStart, i, j, realIndex }) {
const w = this.w;
const dataLabels = new DataLabels(this.w, this.ctx);
if (!this.pointsChart) {
if (w.seriesData.series[i].length > 1) {
this.elPointsMain.node.classList.add("apexcharts-element-hidden");
}
const elPointsWrap = this.markers.plotChartMarkers({
pointsPos,
seriesIndex: realIndex,
j: j + 1
});
if (elPointsWrap !== null) {
this.elPointsMain.add(elPointsWrap);
}
} else {
this.scatter.draw(this.elSeries, j, {
realIndex,
pointsPos,
zRatio: this.zRatio,
elParent: this.elPointsMain
});
}
const drawnLabels = dataLabels.drawDataLabel({
type,
isRangeStart,
pos: pointsPos,
i: realIndex,
j: j + 1
});
if (drawnLabels !== null) {
this.elDataLabelsWrap.add(drawnLabels);
}
}
/** @param {{type: any, series: any, i: any, j: any, x: any, y: any, xArrj: any, yArrj: any, y2: any, y2Arrj: any, pX: any, pY: any, pathState: any, segmentStartX: any, linePath: any, areaPath: any, linePaths: any, areaPaths: any, curve: any, isRangeStart: any}} opts */
_createPaths({
type,
series,
i,
j,
x,
y,
xArrj,
yArrj,
y2,
y2Arrj,
pX,
pY,
pathState,
segmentStartX,
linePath,
areaPath,
linePaths,
areaPaths,
curve,
isRangeStart
}) {
const graphics = new Graphics(this.w);
const areaBottomY = this.areaBottomY;
const rangeArea = type === "rangeArea";
const isLowerRangeAreaPath = type === "rangeArea" && isRangeStart;
switch (curve) {
case "monotoneCubic": {
const yAj = isRangeStart ? yArrj : y2Arrj;
const getSmoothInputs = (xArr, yArr) => {
return xArr.map((_, i2) => {
return [_, yArr[i2]];
}).filter((_) => _[1] !== null);
};
const getSegmentLengths = (yArr) => {
const segLens = [];
let count = 0;
yArr.forEach((_) => {
if (_ !== null) {
count++;
} else if (count > 0) {
segLens.push(count);
count = 0;
}
});
if (count > 0) {
segLens.push(count);
}
return segLens;
};
const getSegments = (yArr, points) => {
const segLens = getSegmentLengths(yArr);
const segments = [];
for (let i2 = 0, len = 0; i2 < segLens.length; len += segLens[i2++]) {
segments[i2] = spline.slice(points, len, len + segLens[i2]);
}
return segments;
};
switch (pathState) {
case 0:
if (yAj[j + 1] === null) {
break;
}
pathState = 1;
// falls through
case 1:
if (!(rangeArea ? xArrj.length === series[i].length : j === series[i].length - 2)) {
break;
}
// falls through
case 2: {
const _xAj = isRangeStart ? xArrj : xArrj.slice().reverse();
const _yAj = isRangeStart ? yAj : yAj.slice().reverse();
const smoothInputs = getSmoothInputs(_xAj, _yAj);
const points = smoothInputs.length > 1 ? spline.points(smoothInputs) : smoothInputs;
let smoothInputsLower = [];
if (rangeArea) {
if (isLowerRangeAreaPath) {
areaPaths = smoothInputs;
} else {
smoothInputsLower = areaPaths.reverse();
}
}
let segmentCount = 0;
let smoothInputsIndex = 0;
getSegments(_yAj, points).forEach((_) => {
segmentCount++;
const svgPoints = svgPath(_);
const _start = smoothInputsIndex;
smoothInputsIndex += _.length;
const _end = smoothInputsIndex - 1;
if (isLowerRangeAreaPath) {
linePath = graphics.move(
smoothInputs[_start][0],
smoothInputs[_start][1]
) + svgPoints;
} else if (rangeArea) {
linePath = graphics.move(
smoothInputsLower[_start][0],
smoothInputsLower[_start][1]
) + graphics.line(
smoothInputs[_start][0],
smoothInputs[_start][1]
) + svgPoints + graphics.line(
smoothInputsLower[_end][0],
smoothInputsLower[_end][1]
);
} else {
linePath = graphics.move(
smoothInputs[_start][0],
smoothInputs[_start][1]
) + svgPoints;
areaPath = linePath + graphics.line(smoothInputs[_end][0], areaBottomY) + graphics.line(smoothInputs[_start][0], areaBottomY) + "z";
areaPaths.push(areaPath);
}
linePaths.push(linePath);
});
if (rangeArea && segmentCount > 1 && !isLowerRangeAreaPath) {
const upperLinePaths = linePaths.slice(segmentCount).reverse();
linePaths.splice(segmentCount);
upperLinePaths.forEach(
(u) => linePaths.push(u)
);
}
pathState = 0;
break;
}
}
break;
}
case "smooth": {
const length = (x - pX) * 0.35;
if (series[i][j] === null) {
pathState = 0;
} else {
switch (pathState) {
case 0:
segmentStartX = pX;
if (isLowerRangeAreaPath) {
linePath = graphics.move(pX, y2Arrj[j]) + graphics.line(pX, pY);
} else {
linePath = graphics.move(pX, pY);
}
areaPath = graphics.move(pX, pY);
if (series[i][j + 1] === null || typeof series[i][j + 1] === "undefined") {
linePaths.push(linePath);
areaPaths.push(areaPath);
break;
}
pathState = 1;
if (j < series[i].length - 2) {
const p = graphics.curve(pX + length, pY, x - length, y, x, y);
linePath += p;
areaPath += p;
break;
}
// falls through
case 1:
if (series[i][j + 1] === null) {
if (isLowerRangeAreaPath) {
linePath += graphics.line(pX, y2);
} else {
linePath += graphics.move(pX, pY);
}
areaPath += graphics.line(pX, areaBottomY) + graphics.line(segmentStartX, areaBottomY) + "z";
linePaths.push(linePath);
areaPaths.push(areaPath);
pathState = -1;
} else {
const p = graphics.curve(pX + length, pY, x - length, y, x, y);
linePath += p;
areaPath += p;
if (j >= series[i].length - 2) {
if (isLowerRangeAreaPath) {
linePath += graphics.curve(x, y, x, y, x, y2) + graphics.move(x, y2);
}
areaPath += graphics.curve(x, y, x, y, x, areaBottomY) + graphics.line(segmentStartX, areaBottomY) + "z";
linePaths.push(linePath);
areaPaths.push(areaPath);
pathState = -1;
}
}
break;
}
}
pX = x;
pY = y;
break;
}
default: {
const pathToPoint = (curve2, x2, y3) => {
let path = "";
switch (curve2) {
case "stepline":
path = graphics.line(x2, null, "H") + graphics.line(null, y3, "V");
break;
case "linestep":
path = graphics.line(null, y3, "V") + graphics.line(x2, null, "H");
break;
case "straight":
path = graphics.line(x2, y3);
break;
}
return path;
};
if (series[i][j] === null) {
pathState = 0;
} else {
switch (pathState) {
case 0:
segmentStartX = pX;
if (isLowerRangeAreaPath) {
linePath = graphics.move(pX, y2Arrj[j]) + graphics.line(pX, pY);
} else {
linePath = graphics.move(pX, pY);
}
areaPath = graphics.move(pX, pY);
if (series[i][j + 1] === null || typeof series[i][j + 1] === "undefined") {
linePaths.push(linePath);
areaPaths.push(areaPath);
break;
}
pathState = 1;
if (j < series[i].length - 2) {
const p = pathToPoint(curve, x, y);
linePath += p;
areaPath += p;
break;
}
// falls through
case 1:
if (series[i][j + 1] === null) {
if (isLowerRangeAreaPath) {
linePath += graphics.line(pX, y2);
} else {
linePath += graphics.move(pX, pY);
}
areaPath += graphics.line(pX, areaBottomY) + graphics.line(segmentStartX, areaBottomY) + "z";
linePaths.push(linePath);
areaPaths.push(areaPath);
pathState = -1;
} else {
const p = pathToPoint(curve, x, y);
linePath += p;
areaPath += p;
if (j >= series[i].length - 2) {
if (isLowerRangeAreaPath) {
linePath += graphics.line(x, y2);
}
areaPath += graphics.line(x, areaBottomY) + graphics.line(segmentStartX, areaBottomY) + "z";
linePaths.push(linePath);
areaPaths.push(areaPath);
pathState = -1;
}
}
break;
}
}
pX = x;
pY = y;
break;
}
}
return {
linePaths,
areaPaths,
pX,
pY,
pathState,
segmentStartX,
linePath,
areaPath
};
}
/**
* @param {any[]} series
* @param {any} pointsPos
* @param {number} i
* @param {number} j
* @param {number} realIndex
*/
handleNullDataPoints(series, pointsPos, i, j, realIndex) {
const w = this.w;
if (series[i][j] === null && w.config.markers.showNullDataPoints || series[i].length === 1) {
let pSize = this.strokeWidth - w.config.markers.strokeWidth / 2;
if (!(pSize > 0)) {
pSize = 0;
}
const elPointsWrap = this.markers.plotChartMarkers({
pointsPos,
seriesIndex: realIndex,
j: j + 1,
pSize,
alwaysDrawMarker: true
});
if (elPointsWrap !== null) {
this.elPointsMain.add(elPointsWrap);
}
}
}
}
class CircularChartsHelpers {
/**
* @param {import('../../../types/internal').ChartStateW} w
*/
constructor(w) {
this.w = w;
}
/**
* @param {number} x
* @param {number} y
* @param {number} i
* @param {string | number} text
*/
drawYAxisTexts(x, y, i, text) {
const w = this.w;
const yaxisConfig = w.config.yaxis[0];
const formatter = w.formatters.yLabelFormatters[0];
const graphics = new Graphics(this.w);
const yaxisLabel = graphics.drawText({
x: x + yaxisConfig.labels.offsetX,
y: y + yaxisConfig.labels.offsetY,
text: formatter(text, i),
textAnchor: "middle",
fontSize: yaxisConfig.labels.style.fontSize,
fontFamily: yaxisConfig.labels.style.fontFamily,
foreColor: Array.isArray(yaxisConfig.labels.style.colors) ? yaxisConfig.labels.style.colors[i] : yaxisConfig.labels.style.colors
});
return yaxisLabel;
}
}
class Pie {
/**
* @param {import('../types/internal').ChartStateW} w
* @param {import('../types/internal').ChartContext} ctx
*/
constructor(w, ctx) {
this.ctx = ctx;
this.w = w;
this.chartType = this.w.config.chart.type;
this.initialAnim = this.w.config.chart.animations.enabled;
this.dynamicAnim = this.initialAnim && this.w.config.chart.animations.dynamicAnimation.enabled;
this.animBeginArr = [0];
this.animDur = 0;
this.donutDataLabels = this.w.config.plotOptions.pie.donut.labels;
this.lineColorArr = w.globals.stroke.colors !== void 0 ? w.globals.stroke.colors : w.globals.colors;
this.defaultSize = Math.min(w.layout.gridWidth, w.layout.gridHeight);
this.centerY = this.defaultSize / 2;
this.centerX = w.layout.gridWidth / 2;
if (w.config.chart.type === "radialBar") {
this.fullAngle = 360;
} else {
this.fullAngle = Math.abs(
w.config.plotOptions.pie.endAngle - w.config.plotOptions.pie.startAngle
);
}
this.initialAngle = w.config.plotOptions.pie.startAngle % this.fullAngle;
w.globals.radialSize = this.defaultSize / 2.05 - w.config.stroke.width - (!w.config.chart.sparkline.enabled ? w.config.chart.dropShadow.blur : 0);
this.donutSize = w.globals.radialSize * parseInt(w.config.plotOptions.pie.donut.size, 10) / 100;
const scaleSize = w.config.plotOptions.pie.customScale;
const halfW = w.layout.gridWidth / 2;
const halfH = w.layout.gridHeight / 2;
this.translateX = halfW - halfW * scaleSize;
this.translateY = halfH - halfH * scaleSize;
this.dataLabelsGroup = new Graphics(this.w).group({
class: "apexcharts-datalabels-group",
transform: `translate(${this.translateX}, ${this.translateY}) scale(${scaleSize})`
});
this.maxY = 0;
this.sliceLabels = [];
this.sliceSizes = [];
this.prevSectorAngleArr = [];
}
/**
* @param {any[]} series
*/
draw(series) {
const self = this;
const w = this.w;
const graphics = new Graphics(this.w);
const elPie = graphics.group({
class: "apexcharts-pie"
});
if (w.globals.noData) return elPie;
let total = 0;
for (let k = 0; k < series.length; k++) {
total += Utils$1.negToZero(series[k]);
}
const sectorAngleArr = [];
const elSeries = graphics.group();
if (total === 0) {
total = 1e-5;
}
series.forEach((m) => {
this.maxY = Math.max(this.maxY, m);
});
if (w.config.yaxis[0].max) {
this.maxY = w.config.yaxis[0].max;
}
if (w.config.grid.position === "back" && this.chartType === "polarArea") {
this.drawPolarElements(elPie);
}
for (let i = 0; i < series.length; i++) {
const angle = this.fullAngle * Utils$1.negToZero(series[i]) / total;
sectorAngleArr.push(angle);
if (this.chartType === "polarArea") {
sectorAngleArr[i] = this.fullAngle / series.length;
this.sliceSizes.push(w.globals.radialSize * series[i] / this.maxY);
} else {
this.sliceSizes.push(w.globals.radialSize);
}
}
if (w.globals.dataChanged) {
let prevTotal = 0;
for (let k = 0; k < w.globals.previousPaths.length; k++) {
prevTotal += Utils$1.negToZero(w.globals.previousPaths[k]);
}
let previousAngle;
for (let i = 0; i < w.globals.previousPaths.length; i++) {
previousAngle = this.fullAngle * Utils$1.negToZero(w.globals.previousPaths[i]) / prevTotal;
this.prevSectorAngleArr.push(previousAngle);
}
}
if (this.donutSize < 0) {
this.donutSize = 0;
}
if (this.chartType === "donut") {
const circle = graphics.drawCircle(this.donutSize);
circle.attr({
cx: this.centerX,
cy: this.centerY,
fill: w.config.plotOptions.pie.donut.background ? w.config.plotOptions.pie.donut.background : "transparent"
});
elSeries.add(circle);
}
const elG = self.drawArcs(sectorAngleArr, series);
this.sliceLabels.forEach((s) => {
elG.add(s);
});
elSeries.attr({
transform: `translate(${this.translateX}, ${this.translateY}) scale(${w.config.plotOptions.pie.customScale})`
});
elSeries.add(elG);
elPie.add(elSeries);
if (this.donutDataLabels.show) {
const dataLabels = this.renderInnerDataLabels(
this.dataLabelsGroup,
this.donutDataLabels,
{
hollowSize: this.donutSize,
centerX: this.centerX,
centerY: this.centerY,
opacity: this.donutDataLabels.show
}
);
elPie.add(dataLabels);
}
if (w.config.grid.position === "front" && this.chartType === "polarArea") {
this.drawPolarElements(elPie);
}
return elPie;
}
// core function for drawing pie arcs
/**
* @param {any[]} sectorAngleArr
* @param {any[]} series
*/
drawArcs(sectorAngleArr, series) {
const w = this.w;
const filters = new Filters(this.w);
const graphics = new Graphics(this.w);
const fill = new Fill(this.w);
const g = graphics.group({
class: "apexcharts-slices"
});
let startAngle = this.initialAngle;
let prevStartAngle = this.initialAngle;
let endAngle = this.initialAngle;
let prevEndAngle = this.initialAngle;
this.strokeWidth = w.config.stroke.show ? w.config.stroke.width : 0;
for (let i = 0; i < sectorAngleArr.length; i++) {
const elPieArc = graphics.group({
class: `apexcharts-series apexcharts-pie-series`,
seriesName: Utils$1.escapeString(w.seriesData.seriesNames[i]),
rel: i + 1,
"data:realIndex": i
});
g.add(elPieArc);
startAngle = endAngle;
prevStartAngle = prevEndAngle;
endAngle = startAngle + sectorAngleArr[i];
prevEndAngle = prevStartAngle + this.prevSectorAngleArr[i];
const angle = endAngle < startAngle ? this.fullAngle + endAngle - startAngle : endAngle - startAngle;
const pathFill = fill.fillPath({
seriesNumber: i,
size: this.sliceSizes[i],
value: series[i]
});
const path = this.getChangedPath(prevStartAngle, prevEndAngle);
const elPath = graphics.drawPath({
d: path,
stroke: Array.isArray(this.lineColorArr) ? this.lineColorArr[i] : this.lineColorArr,
strokeWidth: 0,
fill: pathFill,
fillOpacity: w.config.fill.opacity,
classes: `apexcharts-pie-area apexcharts-${this.chartType.toLowerCase()}-slice-${i}`
});
elPath.attr({
index: 0,
j: i
});
filters.setSelectionFilter(elPath, 0, i);
if (w.config.chart.dropShadow.enabled) {
const shadow = w.config.chart.dropShadow;
filters.dropShadow(elPath, shadow, i);
}
this.addListeners(elPath, this.donutDataLabels);
let labelPosition = {
x: 0,
y: 0
};
const midAngle = (startAngle + angle / 2) % this.fullAngle;
let arcCenter = { x: this.centerX, y: this.centerY };
if (this.chartType === "pie" || this.chartType === "polarArea") {
labelPosition = Utils$1.polarToCartesian(
this.centerX,
this.centerY,
w.globals.radialSize / 1.25 + w.config.plotOptions.pie.dataLabels.offset,
midAngle
);
arcCenter = Utils$1.polarToCartesian(
this.centerX,
this.centerY,
w.globals.radialSize / 2,
midAngle
);
} else if (this.chartType === "donut") {
labelPosition = Utils$1.polarToCartesian(
this.centerX,
this.centerY,
(w.globals.radialSize + this.donutSize) / 2 + w.config.plotOptions.pie.dataLabels.offset,
midAngle
);
arcCenter = Utils$1.polarToCartesian(
this.centerX,
this.centerY,
(w.globals.radialSize + this.donutSize) / 2,
midAngle
);
}
Graphics.setAttrs(elPath.node, {
"data:angle": angle,
"data:startAngle": startAngle,
"data:strokeWidth": this.strokeWidth,
"data:value": series[i],
"data:cx": arcCenter.x,
"data:cy": arcCenter.y
});
elPieArc.add(elPath);
let dur = 0;
if (this.initialAnim && !w.globals.resized && !w.globals.dataChanged) {
dur = angle / this.fullAngle * w.config.chart.animations.speed;
if (dur === 0) dur = 1;
this.animDur = dur + this.animDur;
this.animBeginArr.push(this.animDur);
} else {
this.animBeginArr.push(0);
}
if (this.dynamicAnim && w.globals.dataChanged) {
this.animatePaths(elPath, {
size: this.sliceSizes[i],
endAngle,
startAngle,
prevStartAngle,
prevEndAngle,
animateStartingPos: true,
i,
animBeginArr: this.animBeginArr,
shouldSetPrevPaths: true,
dur: w.config.chart.animations.dynamicAnimation.speed
});
} else {
this.animatePaths(elPath, {
size: this.sliceSizes[i],
endAngle,
startAngle,
i,
totalItems: sectorAngleArr.length - 1,
animBeginArr: this.animBeginArr,
dur
});
}
if (w.config.plotOptions.pie.expandOnClick && this.chartType !== "polarArea") {
elPath.node.addEventListener("mouseup", this.pieClicked.bind(this, i));
}
if (typeof w.interact.selectedDataPoints[0] !== "undefined" && w.interact.selectedDataPoints[0].indexOf(i) > -1) {
this.pieClicked(i);
}
if (w.config.dataLabels.enabled) {
const xPos = labelPosition.x;
const yPos = labelPosition.y;
let text = 100 * angle / this.fullAngle + "%";
if (angle !== 0 && w.config.plotOptions.pie.dataLabels.minAngleToShowLabel < sectorAngleArr[i]) {
const formatter = w.config.dataLabels.formatter;
if (formatter !== void 0) {
text = formatter(w.globals.seriesPercent[i][0], {
seriesIndex: i,
w
});
}
const foreColor = w.globals.dataLabels.style.colors[i];
const elPieLabelWrap = graphics.group({
class: `apexcharts-datalabels`
});
const elPieLabel = graphics.drawText({
x: xPos,
y: yPos,
text,
textAnchor: "middle",
fontSize: w.config.dataLabels.style.fontSize,
fontFamily: w.config.dataLabels.style.fontFamily,
fontWeight: w.config.dataLabels.style.fontWeight,
foreColor
});
elPieLabelWrap.add(elPieLabel);
if (w.config.dataLabels.dropShadow.enabled) {
const textShadow = w.config.dataLabels.dropShadow;
filters.dropShadow(elPieLabel, textShadow);
}
elPieLabel.node.classList.add("apexcharts-pie-label");
if (w.config.chart.animations.animate && w.globals.resized === false) {
elPieLabel.node.classList.add("apexcharts-pie-label-delay");
elPieLabel.node.style.animationDelay = w.config.chart.animations.speed / 940 + "s";
}
this.sliceLabels.push(elPieLabelWrap);
}
}
}
return g;
}
/**
* @param {any} elPath
* @param {Record<string, any>} dataLabels
*/
addListeners(elPath, dataLabels) {
const graphics = new Graphics(this.w);
elPath.node.addEventListener(
"mouseenter",
graphics.pathMouseEnter.bind(this, elPath)
);
elPath.node.addEventListener(
"mouseleave",
graphics.pathMouseLeave.bind(this, elPath)
);
elPath.node.addEventListener(
"mouseleave",
this.revertDataLabelsInner.bind(this)
);
elPath.node.addEventListener(
"mousedown",
graphics.pathMouseDown.bind(this, elPath)
);
if (!this.donutDataLabels.total.showAlways) {
elPath.node.addEventListener(
"mouseenter",
this.printDataLabelsInner.bind(this, elPath.node, dataLabels)
);
elPath.node.addEventListener(
"mousedown",
this.printDataLabelsInner.bind(this, elPath.node, dataLabels)
);
}
}
// This function can be used for other circle charts too
/**
* @param {any} el
* @param {Record<string, any>} opts
*/
animatePaths(el, opts) {
const w = this.w;
const me = this;
let angle = opts.endAngle < opts.startAngle ? this.fullAngle + opts.endAngle - opts.startAngle : opts.endAngle - opts.startAngle;
let prevAngle = angle;
let fromStartAngle = opts.startAngle;
const toStartAngle = opts.startAngle;
if (opts.prevStartAngle !== void 0 && opts.prevEndAngle !== void 0) {
fromStartAngle = opts.prevEndAngle;
prevAngle = opts.prevEndAngle < opts.prevStartAngle ? this.fullAngle + opts.prevEndAngle - opts.prevStartAngle : opts.prevEndAngle - opts.prevStartAngle;
}
if (opts.i === w.config.series.length - 1) {
if (angle + toStartAngle > this.fullAngle) {
opts.endAngle = opts.endAngle - (angle + toStartAngle);
} else if (angle + toStartAngle < this.fullAngle) {
opts.endAngle = opts.endAngle + (this.fullAngle - (angle + toStartAngle));
}
}
if (angle === this.fullAngle) angle = this.fullAngle - 0.01;
me.animateArc(el, fromStartAngle, toStartAngle, angle, prevAngle, opts);
}
/**
* @param {any} el
* @param {number} fromStartAngle
* @param {number} toStartAngle
* @param {number} angle
* @param {number} prevAngle
* @param {Record<string, any>} opts
*/
animateArc(el, fromStartAngle, toStartAngle, angle, prevAngle, opts) {
const me = this;
const w = this.w;
const animations = new Animations(this.w);
const size = opts.size;
let path;
if (isNaN(fromStartAngle) || isNaN(prevAngle)) {
fromStartAngle = toStartAngle;
prevAngle = angle;
opts.dur = 0;
}
let currAngle = angle;
let startAngle = toStartAngle;
const fromAngle = fromStartAngle < toStartAngle ? this.fullAngle + fromStartAngle - toStartAngle : fromStartAngle - toStartAngle;
if (w.globals.dataChanged && opts.shouldSetPrevPaths) {
if (opts.prevEndAngle) {
path = me.getPiePath({
me,
startAngle: opts.prevStartAngle,
angle: opts.prevEndAngle < opts.prevStartAngle ? this.fullAngle + opts.prevEndAngle - opts.prevStartAngle : opts.prevEndAngle - opts.prevStartAngle,
size
});
el.attr({ d: path });
}
}
if (opts.dur !== 0) {
el.animate(opts.dur, opts.animBeginArr[opts.i]).after(
/** @this {any} */
function() {
if (me.chartType === "pie" || me.chartType === "donut" || me.chartType === "polarArea") {
this.animate(
w.config.chart.animations.dynamicAnimation.speed
).attr({
"stroke-width": me.strokeWidth
});
}
if (opts.i === w.config.series.length - 1) {
animations.animationCompleted(el);
}
}
).during((pos) => {
currAngle = fromAngle + (angle - fromAngle) * pos;
if (opts.animateStartingPos) {
currAngle = prevAngle + (angle - prevAngle) * pos;
startAngle = fromStartAngle - prevAngle + (toStartAngle - (fromStartAngle - prevAngle)) * pos;
}
path = me.getPiePath({
me,
startAngle,
angle: currAngle,
size
});
el.node.setAttribute("data:pathOrig", path);
el.attr({
d: path
});
});
} else {
path = me.getPiePath({
me,
startAngle,
angle,
size
});
if (!opts.isTrack) {
w.globals.animationEnded = true;
}
el.node.setAttribute("data:pathOrig", path);
el.attr({
d: path,
"stroke-width": me.strokeWidth
});
}
}
/**
* @param {number} i
*/
pieClicked(i) {
const w = this.w;
const me = this;
const size = me.sliceSizes[i] + (w.config.plotOptions.pie.expandOnClick ? 4 : 0);
const elPath = w.dom.Paper.findOne(
`.apexcharts-${me.chartType.toLowerCase()}-slice-${i}`
);
if (elPath.attr("data:pieClicked") === "true") {
elPath.attr({
"data:pieClicked": "false"
});
this.revertDataLabelsInner();
const origPath = elPath.attr("data:pathOrig");
elPath.attr({
d: origPath
});
return;
} else {
const allEls = w.dom.baseEl.getElementsByClassName("apexcharts-pie-area");
Array.prototype.forEach.call(allEls, (pieSlice) => {
pieSlice.setAttribute("data:pieClicked", "false");
const origPath = pieSlice.getAttribute("data:pathOrig");
if (origPath) {
pieSlice.setAttribute("d", origPath);
}
});
w.interact.capturedDataPointIndex = i;
elPath.attr("data:pieClicked", "true");
}
const startAngle = parseInt(elPath.attr("data:startAngle"), 10);
const angle = parseInt(elPath.attr("data:angle"), 10);
const path = me.getPiePath({
me,
startAngle,
angle,
size
});
if (angle === 360) return;
elPath.plot(path);
}
/**
* @param {number} prevStartAngle
* @param {number} prevEndAngle
*/
getChangedPath(prevStartAngle, prevEndAngle) {
let path = "";
if (this.dynamicAnim && this.w.globals.dataChanged) {
path = this.getPiePath({
me: this,
startAngle: prevStartAngle,
angle: prevEndAngle - prevStartAngle,
// @ts-ignore — size is set dynamically during draw()
size: this.size
});
}
return path;
}
/** @param {{me: any, startAngle: any, angle: any, size: any}} opts */
getPiePath({ me, startAngle, angle, size }) {
let path;
const graphics = new Graphics(this.w);
const startDeg = startAngle;
const startRadians = Math.PI * (startDeg - 90) / 180;
let endDeg = angle + startAngle;
if (Math.ceil(endDeg) >= this.fullAngle + this.w.config.plotOptions.pie.startAngle % this.fullAngle) {
endDeg = this.fullAngle + this.w.config.plotOptions.pie.startAngle % this.fullAngle - 0.01;
}
if (Math.ceil(endDeg) > this.fullAngle) endDeg -= this.fullAngle;
const endRadians = Math.PI * (endDeg - 90) / 180;
const x1 = me.centerX + size * Math.cos(startRadians);
const y1 = me.centerY + size * Math.sin(startRadians);
const x2 = me.centerX + size * Math.cos(endRadians);
const y2 = me.centerY + size * Math.sin(endRadians);
const startInner = Utils$1.polarToCartesian(
me.centerX,
me.centerY,
me.donutSize,
endDeg
);
const endInner = Utils$1.polarToCartesian(
me.centerX,
me.centerY,
me.donutSize,
startDeg
);
const largeArc = angle > 180 ? 1 : 0;
const pathBeginning = ["M", x1, y1, "A", size, size, 0, largeArc, 1, x2, y2];
if (me.chartType === "donut") {
path = [
...pathBeginning,
"L",
startInner.x,
startInner.y,
"A",
me.donutSize,
me.donutSize,
0,
largeArc,
0,
endInner.x,
endInner.y,
"L",
x1,
y1,
"z"
].join(" ");
} else if (me.chartType === "pie" || me.chartType === "polarArea") {
path = [...pathBeginning, "L", me.centerX, me.centerY, "L", x1, y1].join(
" "
);
} else {
path = [...pathBeginning].join(" ");
}
return graphics.roundPathCorners(path, this.strokeWidth * 2);
}
/**
* @param {any} parent
*/
drawPolarElements(parent) {
const w = this.w;
const scale = new Scales(this.w);
const graphics = new Graphics(this.w);
const helpers = new CircularChartsHelpers(this.w);
const gCircles = graphics.group();
const gYAxis = graphics.group();
const yScale = scale.niceScale(0, Math.ceil(this.maxY), 0);
const yTexts = yScale.result.reverse();
const len = yScale.result.length;
this.maxY = yScale.niceMax;
let circleSize = w.globals.radialSize;
const diff = circleSize / (len - 1);
for (let i = 0; i < len - 1; i++) {
const circle = graphics.drawCircle(circleSize);
circle.attr({
cx: this.centerX,
cy: this.centerY,
fill: "none",
"stroke-width": w.config.plotOptions.polarArea.rings.strokeWidth,
stroke: w.config.plotOptions.polarArea.rings.strokeColor
});
if (w.config.yaxis[0].show) {
const yLabel = helpers.drawYAxisTexts(
this.centerX,
this.centerY - circleSize + parseInt(w.config.yaxis[0].labels.style.fontSize, 10) / 2,
i,
yTexts[i]
);
gYAxis.add(yLabel);
}
gCircles.add(circle);
circleSize = circleSize - diff;
}
this.drawSpokes(parent);
parent.add(gCircles);
parent.add(gYAxis);
}
/**
* @param {any} dataLabelsGroup
* @param {Record<string, any>} dataLabelsConfig
* @param {Record<string, any>} opts
*/
renderInnerDataLabels(dataLabelsGroup, dataLabelsConfig, opts) {
const w = this.w;
const graphics = new Graphics(this.w);
const showTotal = dataLabelsConfig.total.show;
dataLabelsGroup.node.innerHTML = "";
dataLabelsGroup.node.style.opacity = opts.opacity;
const x = opts.centerX;
const y = !this.donutDataLabels.total.label ? opts.centerY - opts.centerY / 6 : opts.centerY;
let labelColor, valueColor;
if (dataLabelsConfig.name.color === void 0) {
labelColor = w.globals.colors[0];
} else {
labelColor = dataLabelsConfig.name.color;
}
let labelFontSize = dataLabelsConfig.name.fontSize;
let labelFontFamily = dataLabelsConfig.name.fontFamily;
let labelFontWeight = dataLabelsConfig.name.fontWeight;
if (dataLabelsConfig.value.color === void 0) {
valueColor = w.config.chart.foreColor;
} else {
valueColor = dataLabelsConfig.value.color;
}
const lbFormatter = dataLabelsConfig.value.formatter;
let val = "";
let name2 = "";
if (showTotal) {
labelColor = dataLabelsConfig.total.color;
labelFontSize = dataLabelsConfig.total.fontSize;
labelFontFamily = dataLabelsConfig.total.fontFamily;
labelFontWeight = dataLabelsConfig.total.fontWeight;
name2 = !this.donutDataLabels.total.label ? "" : dataLabelsConfig.total.label;
val = dataLabelsConfig.total.formatter(w);
} else {
if (w.seriesData.series.length === 1) {
val = lbFormatter(w.seriesData.series[0], w);
name2 = w.seriesData.seriesNames[0];
}
}
if (name2) {
name2 = dataLabelsConfig.name.formatter(
name2,
dataLabelsConfig.total.show,
w
);
}
if (dataLabelsConfig.name.show) {
const elLabel = graphics.drawText({
x,
y: y + parseFloat(dataLabelsConfig.name.offsetY),
text: name2,
textAnchor: "middle",
foreColor: labelColor,
fontSize: labelFontSize,
fontWeight: labelFontWeight,
fontFamily: labelFontFamily
});
elLabel.node.classList.add("apexcharts-datalabel-label");
dataLabelsGroup.add(elLabel);
}
if (dataLabelsConfig.value.show) {
const valOffset = dataLabelsConfig.name.show ? parseFloat(dataLabelsConfig.value.offsetY) + 16 : dataLabelsConfig.value.offsetY;
const elValue = graphics.drawText({
x,
y: y + valOffset,
text: val,
textAnchor: "middle",
foreColor: valueColor,
fontWeight: dataLabelsConfig.value.fontWeight,
fontSize: dataLabelsConfig.value.fontSize,
fontFamily: dataLabelsConfig.value.fontFamily
});
elValue.node.classList.add("apexcharts-datalabel-value");
dataLabelsGroup.add(elValue);
}
return dataLabelsGroup;
}
/**
*
* @param {string} name - The name of the series
* @param {string} val - The value of that series
* @param {any} el - Optional el (indicates which series was hovered/clicked). If this param is not present, means we need to show total
* @param {Record<string, any>} labelsConfig
*/
printInnerLabels(labelsConfig, name2, val, el) {
const w = this.w;
let labelColor;
if (el) {
if (labelsConfig.name.color === void 0) {
labelColor = w.globals.colors[parseInt(el.parentNode.getAttribute("rel"), 10) - 1];
} else {
labelColor = labelsConfig.name.color;
}
} else {
if (w.seriesData.series.length > 1 && labelsConfig.total.show) {
labelColor = labelsConfig.total.color;
}
}
const elLabel = w.dom.baseEl.querySelector(".apexcharts-datalabel-label");
const elValue = w.dom.baseEl.querySelector(".apexcharts-datalabel-value");
const lbFormatter = labelsConfig.value.formatter;
val = lbFormatter(val, w);
if (!el && typeof labelsConfig.total.formatter === "function") {
val = labelsConfig.total.formatter(w);
}
const isTotal = name2 === labelsConfig.total.label;
name2 = !this.donutDataLabels.total.label ? "" : labelsConfig.name.formatter(name2, isTotal, w);
if (elLabel !== null) {
elLabel.textContent = name2;
}
if (elValue !== null) {
elValue.textContent = val;
}
if (elLabel !== null) {
const elLabelEl = (
/** @type {HTMLElement} */
elLabel
);
elLabelEl.style.fill = labelColor;
}
}
/**
* @param {any} el
* @param {Record<string, any>} dataLabelsConfig
*/
printDataLabelsInner(el, dataLabelsConfig) {
const w = this.w;
const val = el.getAttribute("data:value");
const name2 = w.seriesData.seriesNames[parseInt(el.parentNode.getAttribute("rel"), 10) - 1];
if (w.seriesData.series.length > 1) {
this.printInnerLabels(dataLabelsConfig, name2, val, el);
}
const dataLabelsGroup = w.dom.baseEl.querySelector(
".apexcharts-datalabels-group"
);
if (dataLabelsGroup !== null) {
const dataLabelsGroupEl = (
/** @type {HTMLElement} */
dataLabelsGroup
);
dataLabelsGroupEl.style.opacity = "1";
}
}
/**
* @param {any} parent
*/
drawSpokes(parent) {
const w = this.w;
const graphics = new Graphics(this.w);
const spokeConfig = w.config.plotOptions.polarArea.spokes;
if (spokeConfig.strokeWidth === 0) return;
const spokes = [];
const angleDivision = 360 / w.seriesData.series.length;
for (let i = 0; i < w.seriesData.series.length; i++) {
spokes.push(
Utils$1.polarToCartesian(
this.centerX,
this.centerY,
w.globals.radialSize,
w.config.plotOptions.pie.startAngle + angleDivision * i
)
);
}
spokes.forEach((p, i) => {
const line = graphics.drawLine(
p.x,
p.y,
this.centerX,
this.centerY,
Array.isArray(spokeConfig.connectorColors) ? spokeConfig.connectorColors[i] : spokeConfig.connectorColors
);
parent.add(line);
});
}
revertDataLabelsInner() {
const w = this.w;
if (this.donutDataLabels.show) {
const dataLabelsGroup = w.dom.Paper.findOne(
`.apexcharts-datalabels-group`
);
const dataLabels = this.renderInnerDataLabels(
dataLabelsGroup,
this.donutDataLabels,
{
hollowSize: this.donutSize,
centerX: this.centerX,
centerY: this.centerY,
opacity: this.donutDataLabels.show
}
);
const elPie = w.dom.Paper.findOne(
".apexcharts-radialbar, .apexcharts-pie"
);
elPie.add(dataLabels);
}
}
}
class Radar {
/**
* @param {import('../types/internal').ChartStateW} w
* @param {import('../types/internal').ChartContext} ctx
*/
constructor(w, ctx) {
this.ctx = ctx;
this.w = w;
this.chartType = this.w.config.chart.type;
this.initialAnim = this.w.config.chart.animations.enabled;
this.dynamicAnim = this.initialAnim && this.w.config.chart.animations.dynamicAnimation.enabled;
this.animDur = 0;
this.graphics = new Graphics(this.w);
this.lineColorArr = w.globals.stroke.colors !== void 0 ? w.globals.stroke.colors : w.globals.colors;
this.defaultSize = w.globals.svgHeight < w.globals.svgWidth ? w.layout.gridHeight : w.layout.gridWidth;
this.isLog = w.config.yaxis[0].logarithmic;
this.logBase = w.config.yaxis[0].logBase;
this.coreUtils = new CoreUtils(this.w);
this.maxValue = this.isLog ? this.coreUtils.getLogVal(this.logBase, w.globals.maxY, 0) : w.globals.maxY;
this.minValue = this.isLog ? this.coreUtils.getLogVal(this.logBase, this.w.globals.minY, 0) : w.globals.minY;
this.polygons = w.config.plotOptions.radar.polygons;
this.strokeWidth = w.config.stroke.show ? w.config.stroke.width : 0;
this.size = this.defaultSize / 2.1 - this.strokeWidth - w.config.chart.dropShadow.blur;
if (w.config.xaxis.labels.show) {
this.size = this.size - w.layout.xAxisLabelsWidth / 1.75;
}
if (w.config.plotOptions.radar.size !== void 0) {
this.size = w.config.plotOptions.radar.size;
}
this.dataRadiusOfPercent = /** @type {any} */
[];
this.dataRadius = /** @type {any} */
[];
this.angleArr = /** @type {any} */
[];
this.dataPointsLen = 0;
this.disAngle = 0;
this.yaxisLabelsTextsPos = [];
}
/**
* @param {any[]} series
*/
draw(series) {
const w = this.w;
const fill = new Fill(this.w);
const allSeries = [];
const dataLabels = new DataLabels(this.w, this.ctx);
if (series.length) {
this.dataPointsLen = series[w.globals.maxValsInArrayIndex].length;
}
this.disAngle = Math.PI * 2 / this.dataPointsLen;
const halfW = w.layout.gridWidth / 2;
const halfH = w.layout.gridHeight / 2;
const translateX = halfW + w.config.plotOptions.radar.offsetX;
const translateY = halfH + w.config.plotOptions.radar.offsetY;
const ret = this.graphics.group({
class: "apexcharts-radar-series apexcharts-plot-series",
transform: `translate(${translateX || 0}, ${translateY || 0})`
});
let dataPointsPos = [];
let elPointsMain = null;
let elDataPointsMain = null;
this.yaxisLabels = this.graphics.group({
class: "apexcharts-yaxis"
});
series.forEach((s, i) => {
const longestSeries = s.length === w.globals.dataPoints;
const elSeries = this.graphics.group().attr({
class: `apexcharts-series`,
"data:longestSeries": longestSeries,
seriesName: Utils$1.escapeString(w.seriesData.seriesNames[i]),
rel: i + 1,
"data:realIndex": i
});
this.dataRadiusOfPercent[i] = [];
this.dataRadius[i] = [];
this.angleArr[i] = [];
s.forEach((dv, j) => {
const range = Math.abs(this.maxValue - this.minValue);
dv = dv - this.minValue;
if (this.isLog) {
dv = this.coreUtils.getLogVal(this.logBase, dv, 0);
}
this.dataRadiusOfPercent[i][j] = dv / range;
this.dataRadius[i][j] = this.dataRadiusOfPercent[i][j] * this.size;
this.angleArr[i][j] = j * this.disAngle;
});
dataPointsPos = this.getDataPointsPos(
this.dataRadius[i],
this.angleArr[i]
);
const paths = this.createPaths(dataPointsPos, {
x: 0,
y: 0
});
elPointsMain = this.graphics.group({
class: "apexcharts-series-markers-wrap apexcharts-element-hidden"
});
elDataPointsMain = this.graphics.group({
class: `apexcharts-datalabels`,
"data:realIndex": i
});
w.globals.delayedElements.push({
el: elPointsMain.node,
index: i
});
const defaultRenderedPathOptions = {
i,
realIndex: i,
animationDelay: i,
initialSpeed: w.config.chart.animations.speed,
dataChangeSpeed: w.config.chart.animations.dynamicAnimation.speed,
className: `apexcharts-radar`,
shouldClipToGrid: false,
bindEventsOnPaths: false,
stroke: w.globals.stroke.colors[i],
strokeLineCap: w.config.stroke.lineCap
};
let pathFrom = null;
if (w.globals.previousPaths.length > 0) {
pathFrom = this.getPreviousPath(i);
}
for (let p = 0; p < paths.linePathsTo.length; p++) {
const renderedLinePath = this.graphics.renderPaths(__spreadProps(__spreadValues({}, defaultRenderedPathOptions), {
pathFrom: pathFrom === null ? paths.linePathsFrom[p] : pathFrom,
pathTo: paths.linePathsTo[p],
strokeWidth: Array.isArray(this.strokeWidth) ? this.strokeWidth[i] : this.strokeWidth,
fill: "none",
drawShadow: false
}));
elSeries.add(renderedLinePath);
const pathFill = fill.fillPath({
seriesNumber: i
});
const renderedAreaPath = this.graphics.renderPaths(__spreadProps(__spreadValues({}, defaultRenderedPathOptions), {
pathFrom: pathFrom === null ? paths.areaPathsFrom[p] : pathFrom,
pathTo: paths.areaPathsTo[p],
strokeWidth: 0,
fill: pathFill,
drawShadow: false
}));
if (w.config.chart.dropShadow.enabled) {
const filters = new Filters(this.w);
const shadow = w.config.chart.dropShadow;
filters.dropShadow(
renderedAreaPath,
Object.assign({}, shadow, { noUserSpaceOnUse: true }),
i
);
}
elSeries.add(renderedAreaPath);
}
s.forEach((sj, j) => {
const markers = new Markers(this.w, this.ctx);
const opts = markers.getMarkerConfig({
cssClass: "apexcharts-marker",
seriesIndex: i,
dataPointIndex: j
});
const point = this.graphics.drawMarker(
dataPointsPos[j].x,
dataPointsPos[j].y,
opts
);
point.attr("rel", j);
point.attr("j", j);
point.attr("index", i);
point.node.setAttribute("default-marker-size", opts.pSize);
const elPointsWrap = this.graphics.group({
class: "apexcharts-series-markers"
});
if (elPointsWrap) {
elPointsWrap.add(point);
}
elPointsMain.add(elPointsWrap);
elSeries.add(elPointsMain);
const dataLabelsConfig = w.config.dataLabels;
if (dataLabelsConfig.enabled) {
const text = dataLabelsConfig.formatter(w.seriesData.series[i][j], {
seriesIndex: i,
dataPointIndex: j,
w
});
dataLabels.plotDataLabelsText({
x: dataPointsPos[j].x,
y: dataPointsPos[j].y,
text,
textAnchor: "middle",
i,
j: i,
parent: elDataPointsMain,
offsetCorrection: false,
dataLabelsConfig: __spreadValues({}, dataLabelsConfig)
});
}
elSeries.add(elDataPointsMain);
});
allSeries.push(elSeries);
});
this.drawPolygons({
parent: ret
});
if (w.config.xaxis.labels.show) {
const xaxisTexts = this.drawXAxisTexts();
ret.add(xaxisTexts);
}
allSeries.forEach((elS) => {
ret.add(elS);
});
ret.add(this.yaxisLabels);
return ret;
}
/**
* @param {Record<string, any>} opts
*/
drawPolygons(opts) {
const w = this.w;
const { parent } = opts;
const helpers = new CircularChartsHelpers(this.w);
const yaxisTexts = w.globals.yAxisScale[0].result.reverse();
const layers = yaxisTexts.length;
const radiusSizes = [];
const layerDis = this.size / (layers - 1);
for (let i = 0; i < layers; i++) {
radiusSizes[i] = layerDis * i;
}
radiusSizes.reverse();
const polygonStrings = [];
const lines = [];
radiusSizes.forEach((radiusSize, r) => {
const polygon = Utils$1.getPolygonPos(radiusSize, this.dataPointsLen);
let string = "";
polygon.forEach((p, i) => {
if (r === 0) {
const line = this.graphics.drawLine(
p.x,
p.y,
0,
0,
Array.isArray(this.polygons.connectorColors) ? this.polygons.connectorColors[i] : this.polygons.connectorColors
);
lines.push(line);
}
if (i === 0) {
this.yaxisLabelsTextsPos.push({
x: p.x,
y: p.y
});
}
string += p.x + "," + p.y + " ";
});
polygonStrings.push(string);
});
polygonStrings.forEach((p, i) => {
const strokeColors = this.polygons.strokeColors;
const strokeWidth = this.polygons.strokeWidth;
const polygon = this.graphics.drawPolygon(
p,
Array.isArray(strokeColors) ? strokeColors[i] : strokeColors,
Array.isArray(strokeWidth) ? strokeWidth[i] : strokeWidth,
w.globals.radarPolygons.fill.colors[i]
);
parent.add(polygon);
});
lines.forEach((l) => {
parent.add(l);
});
if (w.config.yaxis[0].show) {
this.yaxisLabelsTextsPos.forEach(
(p, i) => {
const yText = helpers.drawYAxisTexts(p.x, p.y, i, yaxisTexts[i]);
this.yaxisLabels.add(yText);
}
);
}
}
drawXAxisTexts() {
const w = this.w;
const xaxisLabelsConfig = w.config.xaxis.labels;
const elXAxisWrap = this.graphics.group({
class: "apexcharts-xaxis"
});
const polygonPos = Utils$1.getPolygonPos(this.size, this.dataPointsLen);
w.labelData.labels.forEach((label, i) => {
const formatter = w.config.xaxis.labels.formatter;
const dataLabels = new DataLabels(this.w, this.ctx);
if (polygonPos[i]) {
const textPos = this.getTextPos(polygonPos[i], this.size);
const text = formatter(label, {
seriesIndex: -1,
dataPointIndex: i,
w
});
const dataLabelText = dataLabels.plotDataLabelsText({
x: textPos.newX,
y: textPos.newY,
text,
textAnchor: textPos.textAnchor,
i,
j: i,
parent: elXAxisWrap,
className: "apexcharts-xaxis-label",
color: Array.isArray(xaxisLabelsConfig.style.colors) && xaxisLabelsConfig.style.colors[i] ? xaxisLabelsConfig.style.colors[i] : "#a8a8a8",
dataLabelsConfig: __spreadValues({
textAnchor: textPos.textAnchor,
dropShadow: { enabled: false }
}, xaxisLabelsConfig),
offsetCorrection: false
});
dataLabelText.on("click", (e) => {
if (typeof w.config.chart.events.xAxisLabelClick === "function") {
const opts = Object.assign({}, w, {
labelIndex: i
});
w.config.chart.events.xAxisLabelClick(e, this.ctx, opts);
}
});
}
});
return elXAxisWrap;
}
/**
* @param {Array<Record<string, any>>} pos
* @param {Record<string, any>} origin
*/
createPaths(pos, origin) {
const linePathsTo = [];
let linePathsFrom = [];
const areaPathsTo = [];
let areaPathsFrom = [];
if (pos.length) {
linePathsFrom = [this.graphics.move(origin.x, origin.y)];
areaPathsFrom = [this.graphics.move(origin.x, origin.y)];
let linePathTo = this.graphics.move(pos[0].x, pos[0].y);
let areaPathTo = this.graphics.move(pos[0].x, pos[0].y);
pos.forEach((p, i) => {
linePathTo += this.graphics.line(p.x, p.y);
areaPathTo += this.graphics.line(p.x, p.y);
if (i === pos.length - 1) {
linePathTo += "Z";
areaPathTo += "Z";
}
});
linePathsTo.push(linePathTo);
areaPathsTo.push(areaPathTo);
}
return {
linePathsFrom,
linePathsTo,
areaPathsFrom,
areaPathsTo
};
}
/**
* @param {Record<string, any>} pos
* @param {number} polygonSize
*/
getTextPos(pos, polygonSize) {
const limit = 10;
let textAnchor = "middle";
let newX = pos.x;
let newY = pos.y;
if (Math.abs(pos.x) >= limit) {
if (pos.x > 0) {
textAnchor = "start";
newX += 10;
} else if (pos.x < 0) {
textAnchor = "end";
newX -= 10;
}
} else {
textAnchor = "middle";
}
if (Math.abs(pos.y) >= polygonSize - limit) {
if (pos.y < 0) {
newY -= 10;
} else if (pos.y > 0) {
newY += 10;
}
}
return {
textAnchor,
newX,
newY
};
}
/**
* @param {number} realIndex
*/
getPreviousPath(realIndex) {
const w = this.w;
let pathFrom = null;
for (let pp = 0; pp < w.globals.previousPaths.length; pp++) {
const gpp = w.globals.previousPaths[pp];
if (gpp.paths.length > 0 && parseInt(gpp.realIndex, 10) === parseInt(String(realIndex), 10)) {
if (typeof w.globals.previousPaths[pp].paths[0] !== "undefined") {
pathFrom = w.globals.previousPaths[pp].paths[0].d;
}
}
}
return pathFrom;
}
/**
* @param {any[]} dataRadiusArr
* @param {any[]} angleArr
*/
getDataPointsPos(dataRadiusArr, angleArr, dataPointsLen = this.dataPointsLen) {
dataRadiusArr = dataRadiusArr || [];
angleArr = angleArr || [];
const dataPointsPosArray = [];
for (let j = 0; j < dataPointsLen; j++) {
const curPointPos = {};
curPointPos.x = dataRadiusArr[j] * Math.sin(angleArr[j]);
curPointPos.y = -dataRadiusArr[j] * Math.cos(angleArr[j]);
dataPointsPosArray.push(curPointPos);
}
return dataPointsPosArray;
}
}
class Radial extends Pie {
/**
* @param {import('../types/internal').ChartStateW} w
* @param {import('../types/internal').ChartContext} ctx
*/
constructor(w, ctx) {
super(w, ctx);
this.ctx = ctx;
this.w = w;
this.animBeginArr = [0];
this.animDur = 0;
this.startAngle = w.config.plotOptions.radialBar.startAngle;
this.endAngle = w.config.plotOptions.radialBar.endAngle;
this.totalAngle = Math.abs(
w.config.plotOptions.radialBar.endAngle - w.config.plotOptions.radialBar.startAngle
);
this.trackStartAngle = w.config.plotOptions.radialBar.track.startAngle;
this.trackEndAngle = w.config.plotOptions.radialBar.track.endAngle;
this.barLabels = this.w.config.plotOptions.radialBar.barLabels;
this.donutDataLabels = this.w.config.plotOptions.radialBar.dataLabels;
this.radialDataLabels = this.donutDataLabels;
if (!this.trackStartAngle) this.trackStartAngle = this.startAngle;
if (!this.trackEndAngle) this.trackEndAngle = this.endAngle;
if (this.endAngle === 360) this.endAngle = 359.99;
this.margin = parseInt(w.config.plotOptions.radialBar.track.margin, 10);
this.onBarLabelClick = this.onBarLabelClick.bind(this);
}
/**
* @param {any[]} series
*/
draw(series) {
const w = this.w;
const graphics = new Graphics(this.w);
const ret = graphics.group({
class: "apexcharts-radialbar"
});
if (w.globals.noData) return ret;
const elSeries = graphics.group();
const centerY = this.defaultSize / 2;
const centerX = w.layout.gridWidth / 2;
let size = this.defaultSize / 2.05;
if (!w.config.chart.sparkline.enabled) {
size = size - w.config.stroke.width - w.config.chart.dropShadow.blur;
}
const colorArr = w.globals.fill.colors;
if (w.config.plotOptions.radialBar.track.show) {
const elTracks = this.drawTracks({
size,
centerX,
centerY,
colorArr,
series
});
elSeries.add(elTracks);
}
const elG = this.drawArcs({
size,
centerX,
centerY,
colorArr,
series
});
let totalAngle = 360;
if (w.config.plotOptions.radialBar.startAngle < 0) {
totalAngle = this.totalAngle;
}
const angleRatio = (360 - totalAngle) / 360;
w.globals.radialSize = size - size * angleRatio;
if (this.radialDataLabels.value.show) {
const offset = Math.max(
this.radialDataLabels.value.offsetY,
this.radialDataLabels.name.offsetY
);
w.globals.radialSize += offset * angleRatio;
}
elSeries.add(elG.g);
if (w.config.plotOptions.radialBar.hollow.position === "front") {
elG.g.add(elG.elHollow);
if (elG.dataLabels) {
elG.g.add(elG.dataLabels);
}
}
ret.add(elSeries);
return ret;
}
/**
* @param {Record<string, any>} opts
*/
drawTracks(opts) {
const w = this.w;
const graphics = new Graphics(this.w);
const g = graphics.group({
class: "apexcharts-tracks"
});
const filters = new Filters(this.w);
const fill = new Fill(this.w);
const strokeWidth = this.getStrokeWidth(opts);
opts.size = opts.size - strokeWidth / 2;
for (let i = 0; i < opts.series.length; i++) {
const elRadialBarTrack = graphics.group({
class: "apexcharts-radialbar-track apexcharts-track"
});
g.add(elRadialBarTrack);
elRadialBarTrack.attr({
rel: i + 1
});
opts.size = opts.size - strokeWidth - this.margin;
const trackConfig = w.config.plotOptions.radialBar.track;
const pathFill = fill.fillPath({
seriesNumber: 0,
size: opts.size,
fillColors: Array.isArray(trackConfig.background) ? trackConfig.background[i] : trackConfig.background,
solid: true
});
const startAngle = this.trackStartAngle;
let endAngle = this.trackEndAngle;
if (Math.abs(endAngle) + Math.abs(startAngle) >= 360)
endAngle = 360 - Math.abs(this.startAngle) - 0.1;
const elPath = graphics.drawPath({
d: "",
stroke: pathFill,
strokeWidth: strokeWidth * parseInt(trackConfig.strokeWidth, 10) / 100,
fill: "none",
strokeOpacity: trackConfig.opacity,
classes: "apexcharts-radialbar-area"
});
if (trackConfig.dropShadow.enabled) {
const shadow = trackConfig.dropShadow;
filters.dropShadow(elPath, shadow);
}
elRadialBarTrack.add(elPath);
elPath.attr("id", "apexcharts-radialbarTrack-" + i);
this.animatePaths(elPath, {
centerX: opts.centerX,
centerY: opts.centerY,
endAngle,
startAngle,
size: opts.size,
i,
totalItems: 2,
animBeginArr: 0,
dur: 0,
isTrack: true
});
}
return g;
}
/**
* @param {Record<string, any>} opts
*/
drawArcs(opts) {
const w = this.w;
const graphics = new Graphics(this.w);
const fill = new Fill(this.w);
const filters = new Filters(this.w);
const g = graphics.group();
const strokeWidth = this.getStrokeWidth(opts);
opts.size = opts.size - strokeWidth / 2;
let hollowFillID = w.config.plotOptions.radialBar.hollow.background;
const hollowSize = opts.size - strokeWidth * opts.series.length - this.margin * opts.series.length - strokeWidth * parseInt(w.config.plotOptions.radialBar.track.strokeWidth, 10) / 100 / 2;
const hollowRadius = hollowSize - w.config.plotOptions.radialBar.hollow.margin;
if (w.config.plotOptions.radialBar.hollow.image !== void 0) {
hollowFillID = this.drawHollowImage(opts, g, hollowSize, hollowFillID);
}
const elHollow = this.drawHollow({
size: hollowRadius,
centerX: opts.centerX,
centerY: opts.centerY,
fill: hollowFillID ? hollowFillID : "transparent"
});
if (w.config.plotOptions.radialBar.hollow.dropShadow.enabled) {
const shadow = w.config.plotOptions.radialBar.hollow.dropShadow;
filters.dropShadow(elHollow, shadow);
}
let shown = 1;
if (!this.radialDataLabels.total.show && w.seriesData.series.length > 1) {
shown = 0;
}
let dataLabels = null;
if (this.radialDataLabels.show) {
const dataLabelsGroup = w.dom.Paper.findOne(
`.apexcharts-datalabels-group`
);
dataLabels = this.renderInnerDataLabels(
dataLabelsGroup,
this.radialDataLabels,
{
hollowSize,
centerX: opts.centerX,
centerY: opts.centerY,
opacity: shown
}
);
}
if (w.config.plotOptions.radialBar.hollow.position === "back") {
g.add(elHollow);
if (dataLabels) {
g.add(dataLabels);
}
}
let reverseLoop = false;
if (w.config.plotOptions.radialBar.inverseOrder) {
reverseLoop = true;
}
for (let i = reverseLoop ? opts.series.length - 1 : 0; reverseLoop ? i >= 0 : i < opts.series.length; reverseLoop ? i-- : i++) {
const elRadialBarArc = graphics.group({
class: `apexcharts-series apexcharts-radial-series`,
seriesName: Utils$1.escapeString(w.seriesData.seriesNames[i])
});
g.add(elRadialBarArc);
elRadialBarArc.attr({
rel: i + 1,
"data:realIndex": i
});
Series.addCollapsedClassToSeries(this.w, elRadialBarArc, i);
opts.size = opts.size - strokeWidth - this.margin;
const pathFill = fill.fillPath({
seriesNumber: i,
size: opts.size,
value: opts.series[i]
});
const startAngle = this.startAngle;
let prevStartAngle;
const dataValue = Utils$1.negToZero(opts.series[i] > 100 ? 100 : opts.series[i]) / 100;
let endAngle = Math.round(this.totalAngle * dataValue) + this.startAngle;
let prevEndAngle;
if (w.globals.dataChanged) {
prevStartAngle = this.startAngle;
prevEndAngle = Math.round(
this.totalAngle * Utils$1.negToZero(w.globals.previousPaths[i]) / 100
) + prevStartAngle;
}
const currFullAngle = Math.abs(endAngle) + Math.abs(startAngle);
if (currFullAngle > 360) {
endAngle = endAngle - 0.01;
}
const prevFullAngle = Math.abs(prevEndAngle) + Math.abs(prevStartAngle);
if (prevFullAngle > 360) {
prevEndAngle = prevEndAngle - 0.01;
}
const angle = endAngle - startAngle;
const dashArray = Array.isArray(w.config.stroke.dashArray) ? w.config.stroke.dashArray[i] : w.config.stroke.dashArray;
const elPath = graphics.drawPath({
d: "",
stroke: pathFill,
strokeWidth,
fill: "none",
fillOpacity: w.config.fill.opacity,
classes: "apexcharts-radialbar-area apexcharts-radialbar-slice-" + i,
strokeDashArray: dashArray
});
const radialMidAngle = startAngle + angle / 2;
const radialArcCenter = Utils$1.polarToCartesian(
opts.centerX,
opts.centerY,
opts.size,
radialMidAngle
);
Graphics.setAttrs(elPath.node, {
"data:angle": angle,
"data:value": opts.series[i],
"data:cx": radialArcCenter.x,
"data:cy": radialArcCenter.y
});
if (w.config.chart.dropShadow.enabled) {
const shadow = w.config.chart.dropShadow;
filters.dropShadow(elPath, shadow, i);
}
filters.setSelectionFilter(elPath, 0, i);
this.addListeners(elPath, this.radialDataLabels);
elRadialBarArc.add(elPath);
elPath.attr({
index: 0,
j: i
});
if (this.barLabels.enabled) {
const barStartCords = Utils$1.polarToCartesian(
opts.centerX,
opts.centerY,
opts.size,
startAngle
);
const text = this.barLabels.formatter(w.seriesData.seriesNames[i], {
seriesIndex: i,
w
});
const classes = ["apexcharts-radialbar-label"];
if (!this.barLabels.onClick) {
classes.push("apexcharts-no-click");
}
let textColor = this.barLabels.useSeriesColors ? w.globals.colors[i] : w.config.chart.foreColor;
if (!textColor) {
textColor = w.config.chart.foreColor;
}
const x = barStartCords.x + this.barLabels.offsetX;
const y = barStartCords.y + this.barLabels.offsetY;
const elText = graphics.drawText({
x,
y,
text,
textAnchor: "end",
dominantBaseline: "middle",
fontFamily: this.barLabels.fontFamily,
fontWeight: this.barLabels.fontWeight,
fontSize: this.barLabels.fontSize,
foreColor: textColor,
cssClass: classes.join(" ")
});
elText.on("click", this.onBarLabelClick);
elText.attr({
rel: i + 1
});
if (startAngle !== 0) {
elText.attr({
"transform-origin": `${x} ${y}`,
transform: `rotate(${startAngle} 0 0)`
});
}
elRadialBarArc.add(elText);
}
let dur = 0;
if (this.initialAnim && !w.globals.resized && !w.globals.dataChanged) {
dur = w.config.chart.animations.speed;
}
if (w.globals.dataChanged) {
dur = w.config.chart.animations.dynamicAnimation.speed;
}
this.animDur = dur / (opts.series.length * 1.2) + this.animDur;
this.animBeginArr.push(this.animDur);
this.animatePaths(elPath, {
centerX: opts.centerX,
centerY: opts.centerY,
endAngle,
startAngle,
prevEndAngle,
prevStartAngle,
size: opts.size,
i,
totalItems: 2,
animBeginArr: this.animBeginArr,
dur,
shouldSetPrevPaths: true
});
}
return {
g,
elHollow,
dataLabels
};
}
/**
* @param {Record<string, any>} opts
*/
drawHollow(opts) {
const graphics = new Graphics(this.w);
const circle = graphics.drawCircle(opts.size * 2);
circle.attr({
class: "apexcharts-radialbar-hollow",
cx: opts.centerX,
cy: opts.centerY,
r: opts.size,
fill: opts.fill
});
return circle;
}
/**
* @param {Record<string, any>} opts
* @param {any} g
* @param {number} hollowSize
* @param {string} hollowFillID
*/
drawHollowImage(opts, g, hollowSize, hollowFillID) {
const w = this.w;
const fill = new Fill(this.w);
const randID = Utils$1.randomId();
const hollowFillImg = w.config.plotOptions.radialBar.hollow.image;
if (w.config.plotOptions.radialBar.hollow.imageClipped) {
fill.clippedImgArea({
width: hollowSize,
height: hollowSize,
image: hollowFillImg,
patternID: `pattern${w.globals.cuid}${randID}`
});
hollowFillID = `url(#pattern${w.globals.cuid}${randID})`;
} else {
const imgWidth = w.config.plotOptions.radialBar.hollow.imageWidth;
const imgHeight = w.config.plotOptions.radialBar.hollow.imageHeight;
if (imgWidth === void 0 && imgHeight === void 0) {
const image = w.dom.Paper.image(
hollowFillImg,
/** @this {any} */
function(loader) {
this.move(
opts.centerX - loader.width / 2 + w.config.plotOptions.radialBar.hollow.imageOffsetX,
opts.centerY - loader.height / 2 + w.config.plotOptions.radialBar.hollow.imageOffsetY
);
}
);
g.add(image);
} else {
const image = w.dom.Paper.image(
hollowFillImg,
/** @this {any} */
function() {
this.move(
opts.centerX - imgWidth / 2 + w.config.plotOptions.radialBar.hollow.imageOffsetX,
opts.centerY - imgHeight / 2 + w.config.plotOptions.radialBar.hollow.imageOffsetY
);
this.size(imgWidth, imgHeight);
}
);
g.add(image);
}
}
return hollowFillID;
}
/**
* @param {Record<string, any>} opts
*/
getStrokeWidth(opts) {
const w = this.w;
return opts.size * (100 - parseInt(w.config.plotOptions.radialBar.hollow.size, 10)) / 100 / (opts.series.length + 1) - this.margin;
}
/**
* @param {Event} e
*/
onBarLabelClick(e) {
var _a;
const target = (
/** @type {Element} */
e.target
);
const seriesIndex = parseInt((_a = target.getAttribute("rel")) != null ? _a : "", 10) - 1;
const legendClick = this.barLabels.onClick;
const w = this.w;
if (legendClick) {
legendClick(w.seriesData.seriesNames[seriesIndex], { w, seriesIndex });
}
}
}
class RangeBar extends Bar {
/**
* @param {any[]} series
* @param {number} seriesIndex
*/
draw(series, seriesIndex) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
const w = this.w;
const graphics = new Graphics(this.w);
this.rangeBarOptions = this.w.config.plotOptions.rangeBar;
this.series = series;
this.seriesRangeStart = w.rangeData.seriesRangeStart;
this.seriesRangeEnd = w.rangeData.seriesRangeEnd;
this.barHelpers.initVariables(series);
const ret = graphics.group({
class: "apexcharts-rangebar-series apexcharts-plot-series"
});
for (let i = 0; i < series.length; i++) {
let x, y;
const realIndex = w.globals.comboCharts ? (
/** @type {any} */
seriesIndex[i]
) : i;
const { columnGroupIndex } = this.barHelpers.getGroupIndex(realIndex);
const elSeries = graphics.group({
class: `apexcharts-series`,
seriesName: Utils$1.escapeString(w.seriesData.seriesNames[realIndex]),
rel: i + 1,
"data:realIndex": realIndex
});
Series.addCollapsedClassToSeries(this.w, elSeries, realIndex);
if (series[i].length > 0) {
this.visibleI = this.visibleI + 1;
}
let translationsIndex = 0;
if (this.yRatio.length > 1) {
this.yaxisIndex = /** @type {any} */
w.globals.seriesYAxisReverseMap[realIndex][0];
translationsIndex = realIndex;
}
const initPositions = this.barHelpers.initialPositions(realIndex);
const {
y: initY,
zeroW,
// zeroW is the baseline where 0 meets x axis
x: initX,
zeroH
// zeroH is the baseline where 0 meets y axis
} = initPositions;
let barWidth = (_a = initPositions.barWidth) != null ? _a : 0;
let barHeight = (_b = initPositions.barHeight) != null ? _b : 0;
const yDivision = (_c = initPositions.yDivision) != null ? _c : 0;
const xDivision = (_d = initPositions.xDivision) != null ? _d : 0;
y = initY;
x = initX;
const elDataLabelsWrap = graphics.group({
class: "apexcharts-datalabels",
"data:realIndex": realIndex
});
const elGoalsMarkers = graphics.group({
class: "apexcharts-rangebar-goals-markers"
});
for (let j = 0; j < w.globals.dataPoints; j++) {
const strokeWidth = this.barHelpers.getStrokeWidth(i, j, realIndex);
const y1 = this.seriesRangeStart[i][j];
const y2 = this.seriesRangeEnd[i][j];
let paths = (
/** @type {any} */
null
);
let barXPosition = null;
let barYPosition = null;
const params = { x, y, strokeWidth, elSeries };
let seriesLen = this.seriesLen;
if (w.config.plotOptions.bar.rangeBarGroupRows) {
seriesLen = 1;
}
if (typeof /** @type {Record<string,any>} */
((_e = w.config.series[i].data) == null ? void 0 : _e[j]) === "undefined") {
break;
}
if (this.isHorizontal) {
barYPosition = y + barHeight * /** @type {any} */
this.visibleI;
const srty = (yDivision - barHeight * seriesLen) / 2;
if (
/** @type {Record<string,any>} */
(_g = (_f = w.config.series[i].data) == null ? void 0 : _f[j]) == null ? void 0 : _g.x
) {
const positions = this.detectOverlappingBars({
i,
j,
barYPosition,
srty,
barHeight,
yDivision,
initPositions
});
barHeight = positions.barHeight;
barYPosition = positions.barYPosition;
}
paths = this.drawRangeBarPaths(__spreadValues({
indexes: { i, j, realIndex },
barHeight,
barYPosition,
zeroW,
yDivision,
y1,
y2
}, params));
barWidth = paths.barWidth;
} else {
if (w.axisFlags.isXNumeric) {
x = (w.seriesData.seriesX[i][j] - w.globals.minX) / this.xRatio - barWidth / 2;
}
barXPosition = x + barWidth * /** @type {any} */
this.visibleI;
const srtx = (xDivision - barWidth * seriesLen) / 2;
if (
/** @type {Record<string,any>} */
(_i = (_h = w.config.series[i].data) == null ? void 0 : _h[j]) == null ? void 0 : _i.x
) {
const positions = this.detectOverlappingBars({
i,
j,
barXPosition,
srtx,
barWidth,
xDivision,
initPositions
});
barWidth = positions.barWidth;
barXPosition = positions.barXPosition;
}
paths = this.drawRangeColumnPaths(__spreadValues({
indexes: { i, j, realIndex, translationsIndex },
barWidth,
barXPosition,
zeroH,
xDivision
}, params));
barHeight = paths.barHeight;
}
const barGoalLine = this.barHelpers.drawGoalLine({
barXPosition: paths.barXPosition,
barYPosition,
goalX: paths.goalX,
goalY: paths.goalY,
barHeight,
barWidth
});
if (barGoalLine) {
elGoalsMarkers.add(barGoalLine);
}
y = paths.y;
x = paths.x;
const pathFill = this.barHelpers.getPathFillColor(
series,
i,
j,
realIndex
);
this.renderSeries({
realIndex,
pathFill: pathFill.color,
lineFill: pathFill.useRangeColor ? pathFill.color : w.globals.stroke.colors[realIndex],
j,
i,
x,
y,
y1,
y2,
pathFrom: paths.pathFrom,
pathTo: paths.pathTo,
strokeWidth,
elSeries,
series,
barHeight,
barWidth,
barXPosition,
barYPosition,
columnGroupIndex,
elDataLabelsWrap,
elGoalsMarkers,
visibleSeries: this.visibleI,
type: "rangebar"
});
}
ret.add(elSeries);
}
return ret;
}
/** @param {{ i?: any, j?: any, barYPosition?: any, barXPosition?: any, srty?: any, srtx?: any, barHeight?: any, barWidth?: any, yDivision?: any, xDivision?: any, initPositions?: any }} opts */
detectOverlappingBars({
i,
j,
barYPosition,
barXPosition,
srty,
srtx,
barHeight,
barWidth,
yDivision,
xDivision,
initPositions
}) {
var _a, _b, _c, _d;
const w = this.w;
let overlaps = [];
const rangeName = (
/** @type {Record<string,any>} */
(_b = (_a = w.config.series[i].data) == null ? void 0 : _a[j]) == null ? void 0 : _b.rangeName
);
const x = (
/** @type {Record<string,any>} */
(_d = (_c = w.config.series[i].data) == null ? void 0 : _c[j]) == null ? void 0 : _d.x
);
const labelX = Array.isArray(x) ? x.join(" ") : x;
const rowIndex = w.labelData.labels.map((_) => Array.isArray(_) ? _.join(" ") : _).indexOf(labelX);
const overlappedIndex = w.rangeData.seriesRange[i].findIndex(
(tx) => {
var _a2;
return tx.x === labelX && ((_a2 = tx.overlaps) == null ? void 0 : _a2.size) > 0;
}
);
if (this.isHorizontal) {
if (w.config.plotOptions.bar.rangeBarGroupRows) {
barYPosition = srty + yDivision * rowIndex;
} else {
barYPosition = srty + barHeight * this.visibleI + yDivision * rowIndex;
}
if (overlappedIndex > -1 && !w.config.plotOptions.bar.rangeBarOverlap) {
overlaps = Array.from(
/** @type {any} */
w.rangeData.seriesRange[i][overlappedIndex].overlaps
);
if (overlaps.indexOf(rangeName) > -1) {
barHeight = initPositions.barHeight / overlaps.length;
barYPosition = barHeight * this.visibleI + yDivision * (100 - parseInt(this.barOptions.barHeight, 10)) / 100 / 2 + barHeight * (this.visibleI + overlaps.indexOf(rangeName)) + yDivision * rowIndex;
}
}
} else {
if (rowIndex > -1 && !w.labelData.timescaleLabels.length) {
if (w.config.plotOptions.bar.rangeBarGroupRows) {
barXPosition = srtx + xDivision * rowIndex;
} else {
barXPosition = srtx + barWidth * this.visibleI + xDivision * rowIndex;
}
}
if (overlappedIndex > -1 && !w.config.plotOptions.bar.rangeBarOverlap) {
overlaps = Array.from(
/** @type {any} */
w.rangeData.seriesRange[i][overlappedIndex].overlaps
);
if (overlaps.indexOf(rangeName) > -1) {
barWidth = initPositions.barWidth / overlaps.length;
barXPosition = barWidth * this.visibleI + xDivision * (100 - parseInt(this.barOptions.barWidth, 10)) / 100 / 2 + barWidth * (this.visibleI + overlaps.indexOf(rangeName)) + xDivision * rowIndex;
}
}
}
return {
barYPosition,
barXPosition,
barHeight,
barWidth
};
}
/** @param {{indexes: any, x: any, xDivision: any, barWidth: any, barXPosition: any, zeroH: any}} opts */
drawRangeColumnPaths({
indexes,
x,
xDivision,
barWidth,
barXPosition,
zeroH
}) {
var _a, _b;
const w = this.w;
const { i, j, realIndex, translationsIndex } = indexes;
const yRatio = this.yRatio[translationsIndex];
const range = this.getRangeValue(realIndex, j);
let y1 = Math.min(range.start, range.end);
let y2 = Math.max(range.start, range.end);
if (typeof /** @type {any} */
((_a = this.series[i]) == null ? void 0 : _a[j]) === "undefined" || /** @type {any} */
((_b = this.series[i]) == null ? void 0 : _b[j]) === null) {
y1 = zeroH;
} else {
y1 = zeroH - y1 / yRatio;
y2 = zeroH - y2 / yRatio;
}
const barHeight = Math.abs(y2 - y1);
const paths = this.barHelpers.getColumnPaths({
barXPosition,
barWidth,
y1,
y2,
strokeWidth: this.strokeWidth,
series: this.seriesRangeEnd,
realIndex,
i: realIndex,
j,
w
});
if (!w.axisFlags.isXNumeric) {
x = x + xDivision;
} else {
const xForNumericXAxis = this.getBarXForNumericXAxis({
x,
j,
realIndex,
barWidth
});
x = xForNumericXAxis.x;
barXPosition = xForNumericXAxis.barXPosition;
}
return {
pathTo: paths.pathTo,
pathFrom: paths.pathFrom,
barHeight,
x,
y: range.start < 0 && range.end < 0 ? y1 : y2,
goalY: this.barHelpers.getGoalValues(
"y",
/** @type {any} */
null,
zeroH,
i,
j,
translationsIndex
),
barXPosition
};
}
/**
* @param {number} val
*/
preventBarOverflow(val) {
const w = this.w;
if (val < 0) {
val = 0;
}
if (val > w.layout.gridWidth) {
val = w.layout.gridWidth;
}
return val;
}
/** @param {{indexes: any, y: any, y1: any, y2: any, yDivision: any, barHeight: any, barYPosition: any, zeroW: any}} opts */
drawRangeBarPaths({
indexes,
y,
y1,
y2,
yDivision,
barHeight,
barYPosition,
zeroW
}) {
const w = this.w;
const { realIndex, j } = indexes;
const x1 = this.preventBarOverflow(zeroW + y1 / this.invertedYRatio);
const x2 = this.preventBarOverflow(zeroW + y2 / this.invertedYRatio);
const range = this.getRangeValue(realIndex, j);
const barWidth = Math.abs(x2 - x1);
const paths = this.barHelpers.getBarpaths({
barYPosition,
barHeight,
x1,
x2,
strokeWidth: this.strokeWidth,
series: this.seriesRangeEnd,
i: realIndex,
realIndex,
j,
w
});
if (!w.axisFlags.isXNumeric) {
y = y + yDivision;
}
return {
pathTo: paths.pathTo,
pathFrom: paths.pathFrom,
barWidth,
x: range.start < 0 && range.end < 0 ? x1 : x2,
goalX: this.barHelpers.getGoalValues(
"x",
zeroW,
/** @type {any} */
null,
realIndex,
j,
0
),
y
};
}
/**
* @param {number} i
* @param {number} j
*/
getRangeValue(i, j) {
const w = this.w;
return {
start: w.rangeData.seriesRangeStart[i][j],
end: w.rangeData.seriesRangeEnd[i][j]
};
}
}
function normalize(data, area) {
let sum = 0;
for (let i = 0; i < data.length; i++) {
sum += data[i];
}
const multiplier = area / sum;
const result = new Array(data.length);
for (let i = 0; i < data.length; i++) {
result[i] = data[i] * multiplier;
}
return result;
}
function calculateRatio(rowMin, rowMax, rowSum, length) {
const lengthSq = length * length;
const sumSq = rowSum * rowSum;
return Math.max(
lengthSq * rowMax / sumSq,
sumSq / (lengthSq * rowMin)
);
}
function improvesRatio(rowLen, rowMin, rowMax, rowSum, nextNode, length) {
if (rowLen === 0) return true;
const currentRatio = calculateRatio(rowMin, rowMax, rowSum, length);
const newRatio = calculateRatio(
Math.min(rowMin, nextNode),
Math.max(rowMax, nextNode),
rowSum + nextNode,
length
);
return currentRatio >= newRatio;
}
function emitCoordinates(coords, row, rowLen, rowSum, xoffset, yoffset, width, height) {
if (width >= height) {
const areaWidth = rowSum / height;
let subY = yoffset;
for (let i = 0; i < rowLen; i++) {
const h = row[i] / areaWidth;
coords.push([xoffset, subY, xoffset + areaWidth, subY + h]);
subY += h;
}
} else {
const areaHeight = rowSum / width;
let subX = xoffset;
for (let i = 0; i < rowLen; i++) {
const w = row[i] / areaHeight;
coords.push([subX, yoffset, subX + w, yoffset + areaHeight]);
subX += w;
}
}
}
function squarify(data, xoffset, yoffset, width, height) {
const coords = [];
const n = data.length;
if (n === 0) return coords;
const row = new Array(n);
let rowLen = 0;
let rowSum = 0;
let rowMin = Infinity;
let rowMax = -Infinity;
let i = 0;
while (i < n) {
const length = Math.min(width, height);
const val = data[i];
if (improvesRatio(rowLen, rowMin, rowMax, rowSum, val, length)) {
row[rowLen] = val;
rowLen++;
rowSum += val;
if (val < rowMin) rowMin = val;
if (val > rowMax) rowMax = val;
i++;
} else {
emitCoordinates(coords, row, rowLen, rowSum, xoffset, yoffset, width, height);
if (width >= height) {
const areaWidth = rowSum / height;
xoffset += areaWidth;
width -= areaWidth;
} else {
const areaHeight = rowSum / width;
yoffset += areaHeight;
height -= areaHeight;
}
rowLen = 0;
rowSum = 0;
rowMin = Infinity;
rowMax = -Infinity;
}
}
if (rowLen > 0) {
emitCoordinates(coords, row, rowLen, rowSum, xoffset, yoffset, width, height);
}
return coords;
}
function generate(data, width, height) {
const n = data.length;
const sums = new Array(n);
for (let i = 0; i < n; i++) {
let s = 0;
const series = data[i];
for (let j = 0; j < series.length; j++) {
s += series[j];
}
sums[i] = s;
}
const seriesRects = squarify(
normalize(sums, width * height),
0,
0,
width,
height
);
const results = new Array(n);
for (let i = 0; i < n; i++) {
const rect = seriesRects[i];
const rx = rect[0];
const ry = rect[1];
const rw = rect[2] - rx;
const rh = rect[3] - ry;
results[i] = squarify(
normalize(data[i], rw * rh),
rx,
ry,
rw,
rh
);
}
return results;
}
const TreemapSquared = { generate };
class TreemapChart {
/**
* @param {import('../types/internal').ChartStateW} w
* @param {import('../types/internal').ChartContext} ctx
*/
constructor(w, ctx) {
this.ctx = ctx;
this.w = w;
this.strokeWidth = this.w.config.stroke.width;
this.helpers = new TreemapHelpers(w, ctx);
this.dynamicAnim = this.w.config.chart.animations.dynamicAnimation;
this.labels = [];
}
/**
* @param {any[]} series
*/
draw(series) {
const w = this.w;
const graphics = new Graphics(this.w, this.ctx);
const fill = new Fill(this.w);
const ret = graphics.group({
class: "apexcharts-treemap"
});
if (w.globals.noData) return ret;
const ser = [];
series.forEach((s) => {
const d = s.map((v) => {
return Math.abs(v);
});
ser.push(d);
});
this.negRange = this.helpers.checkColorRange();
w.config.series.forEach((s, i) => {
s.data.forEach((l) => {
if (!Array.isArray(this.labels[i])) this.labels[i] = [];
this.labels[i].push(l.x);
});
});
const nodes = TreemapSquared.generate(
ser,
w.layout.gridWidth,
w.layout.gridHeight
);
nodes.forEach((node, i) => {
var _a;
const elSeries = graphics.group({
class: `apexcharts-series apexcharts-treemap-series`,
seriesName: Utils$1.escapeString(w.seriesData.seriesNames[i]),
rel: i + 1,
"data:realIndex": i
});
graphics.setupEventDelegation(elSeries, ".apexcharts-treemap-rect");
if (w.config.chart.dropShadow.enabled) {
const shadow = w.config.chart.dropShadow;
const filters = new Filters(this.w);
filters.dropShadow(ret, shadow, i);
}
const elDataLabelWrap = graphics.group({
class: "apexcharts-data-labels"
});
const bounds = {
xMin: Infinity,
yMin: Infinity,
xMax: -Infinity,
yMax: -Infinity
};
node.forEach((r, j) => {
const x1 = r[0];
const y1 = r[1];
const x2 = r[2];
const y2 = r[3];
bounds.xMin = Math.min(bounds.xMin, x1);
bounds.yMin = Math.min(bounds.yMin, y1);
bounds.xMax = Math.max(bounds.xMax, x2);
bounds.yMax = Math.max(bounds.yMax, y2);
const colorProps = this.helpers.getShadeColor(
w.config.chart.type,
i,
j,
this.negRange
);
const color = colorProps.color;
const pathFill = fill.fillPath({
color,
seriesNumber: i,
dataPointIndex: j
});
const elRect = graphics.drawRect(
x1,
y1,
x2 - x1,
y2 - y1,
w.config.plotOptions.treemap.borderRadius,
"#fff",
1,
this.strokeWidth,
w.config.plotOptions.treemap.useFillColorAsStroke ? color : w.globals.stroke.colors[i]
);
elRect.attr({
cx: x1,
cy: y1,
index: i,
i,
j,
width: x2 - x1,
height: y2 - y1,
fill: pathFill
});
elRect.node.classList.add("apexcharts-treemap-rect");
let fromRect = {
x: x1 + (x2 - x1) / 2,
y: y1 + (y2 - y1) / 2,
width: 0,
height: 0
};
const toRect = {
x: x1,
y: y1,
width: x2 - x1,
height: y2 - y1
};
if (w.config.chart.animations.enabled && !w.globals.dataChanged) {
let speed = 1;
if (!w.globals.resized) {
speed = w.config.chart.animations.speed;
}
this.animateTreemap(elRect, fromRect, toRect, speed);
}
if (w.globals.dataChanged) {
let speed = 1;
if (this.dynamicAnim.enabled && w.globals.shouldAnimate) {
speed = this.dynamicAnim.speed;
if (w.globals.previousPaths[i] && /** @type {Record<string,any>} */
w.globals.previousPaths[i][j] && /** @type {Record<string,any>} */
w.globals.previousPaths[i][j].rect) {
fromRect = /** @type {Record<string,any>} */
w.globals.previousPaths[i][j].rect;
}
this.animateTreemap(elRect, fromRect, toRect, speed);
}
}
let fontSize = this.getFontSize(r);
let formattedText = w.config.dataLabels.formatter(this.labels[i][j], {
value: w.seriesData.series[i][j],
seriesIndex: i,
dataPointIndex: j,
w
});
if (w.config.plotOptions.treemap.dataLabels.format === "truncate") {
fontSize = parseInt(String(w.config.dataLabels.style.fontSize), 10);
formattedText = this.truncateLabels(
String(formattedText),
fontSize,
x1,
y1,
x2,
y2
);
}
let dataLabels = null;
if (w.seriesData.series[i][j]) {
dataLabels = this.helpers.calculateDataLabels({
text: formattedText,
x: (x1 + x2) / 2,
y: (y1 + y2) / 2 + this.strokeWidth / 2 + fontSize / 3,
i,
j,
colorProps,
fontSize,
series
});
}
if (w.config.dataLabels.enabled && dataLabels) {
this.rotateToFitLabel(
dataLabels,
fontSize,
formattedText,
x1,
y1,
x2,
y2
);
}
elSeries.add(elRect);
if (dataLabels !== null) {
elSeries.add(dataLabels);
}
});
const seriesTitle = w.config.plotOptions.treemap.seriesTitle;
if (w.config.series.length > 1 && seriesTitle && seriesTitle.show) {
const sName = (
/** @type {Record<string,any>} */
w.config.series[i].name || ""
);
if (sName && bounds.xMin < Infinity && bounds.yMin < Infinity) {
const {
offsetX,
offsetY,
borderColor,
borderWidth,
borderRadius,
style
} = seriesTitle;
const textColor = style.color || w.config.chart.foreColor;
const padding = {
left: style.padding.left,
right: style.padding.right,
top: style.padding.top,
bottom: style.padding.bottom
};
const textSize = graphics.getTextRects(
sName,
style.fontSize,
style.fontFamily
);
const labelRectWidth = textSize.width + padding.left + padding.right;
const labelRectHeight = textSize.height + padding.top + padding.bottom;
const labelX = bounds.xMin + (offsetX || 0);
const labelY = bounds.yMin + (offsetY || 0);
const elLabelRect = graphics.drawRect(
labelX,
labelY,
labelRectWidth,
labelRectHeight,
borderRadius,
style.background,
1,
borderWidth,
borderColor
);
const elLabelText = graphics.drawText({
x: labelX + padding.left,
y: labelY + padding.top + ((_a = textSize == null ? void 0 : textSize.height) != null ? _a : 0) * 0.75,
text: sName,
fontSize: style.fontSize,
fontFamily: style.fontFamily,
fontWeight: style.fontWeight,
foreColor: textColor,
cssClass: style.cssClass || ""
});
elSeries.add(elLabelRect);
elSeries.add(elLabelText);
}
}
elSeries.add(elDataLabelWrap);
ret.add(elSeries);
});
return ret;
}
// This calculates a font-size based upon
// average label length and the size of the box
/**
* @param {number[]} coordinates
*/
getFontSize(coordinates) {
const w = this.w;
function totalLabelLength(arr) {
let i, total = 0;
if (Array.isArray(arr[0])) {
for (i = 0; i < arr.length; i++) {
total += totalLabelLength(arr[i]);
}
} else {
for (i = 0; i < arr.length; i++) {
total += arr[i].length;
}
}
return total;
}
function countLabels(arr) {
let i, total = 0;
if (Array.isArray(arr[0])) {
for (i = 0; i < arr.length; i++) {
total += countLabels(arr[i]);
}
} else {
for (i = 0; i < arr.length; i++) {
total += 1;
}
}
return total;
}
const averagelabelsize = totalLabelLength(this.labels) / countLabels(this.labels);
function fontSize(width, height) {
const area = width * height;
const arearoot = Math.pow(area, 0.5);
return Math.min(
arearoot / averagelabelsize,
parseInt(w.config.dataLabels.style.fontSize, 10)
);
}
return fontSize(
coordinates[2] - coordinates[0],
coordinates[3] - coordinates[1]
);
}
/**
* @param {any} elText
* @param {string | number} fontSize
* @param {string} text
* @param {number} x1
* @param {number} y1
* @param {number} x2
* @param {number} y2
*/
rotateToFitLabel(elText, fontSize, text, x1, y1, x2, y2) {
const graphics = new Graphics(this.w);
const textRect = graphics.getTextRects(text, String(fontSize));
if (textRect.width + this.w.config.stroke.width + 5 > x2 - x1 && textRect.width <= y2 - y1) {
const labelRotatingCenter = graphics.rotateAroundCenter(elText.node);
elText.node.setAttribute(
"transform",
`rotate(-90 ${labelRotatingCenter.x} ${labelRotatingCenter.y}) translate(${textRect.height / 3})`
);
}
}
// This is an alternative label formatting method that uses a
// consistent font size, and trims the edge of long labels
/**
* @param {string} text
* @param {number} fontSize
* @param {number} x1
* @param {number} y1
* @param {number} x2
* @param {number} y2
*/
truncateLabels(text, fontSize, x1, y1, x2, y2) {
const graphics = new Graphics(this.w);
const textRect = graphics.getTextRects(text, String(fontSize));
const labelMaxWidth = textRect.width + this.w.config.stroke.width + 5 > x2 - x1 && y2 - y1 > x2 - x1 ? y2 - y1 : x2 - x1;
const truncatedText = graphics.getTextBasedOnMaxWidth({
text,
maxWidth: labelMaxWidth,
fontSize
});
if (text.length !== truncatedText.length && labelMaxWidth / fontSize < 5) {
return "";
} else {
return truncatedText;
}
}
/**
* @param {any} el
* @param {Record<string, any>} fromRect
* @param {Record<string, any>} toRect
* @param {number} speed
*/
animateTreemap(el, fromRect, toRect, speed) {
const animations = new Animations(this.w);
animations.animateRect(el, fromRect, toRect, speed, () => {
animations.animationCompleted(el);
});
}
}
ApexCharts.use({
line: Line,
area: Line,
scatter: Line,
bubble: Line,
rangeArea: Line,
bar: Bar,
column: Bar,
barStacked: BarStacked,
rangeBar: RangeBar,
candlestick: BoxCandleStick,
boxPlot: BoxCandleStick,
pie: Pie,
donut: Pie,
polarArea: Pie,
radialBar: Radial,
radar: Radar,
heatmap: HeatMap,
treemap: TreemapChart
});
class SSRRenderer {
/**
* Render chart to SVG string for server-side rendering
*
* @param {Record<string, any>} options - Chart configuration (same as ApexCharts constructor)
* @param {{ width?: number, height?: number, scale?: number }} [ssrOptions] - SSR-specific options
* @returns {Promise<string>} SVG string
*
* @example
* const svgString = await SSRRenderer.renderToString({
* series: [{ data: [30, 40, 35] }],
* chart: { type: 'bar' }
* }, {
* width: 500,
* height: 300
* });
*/
static renderToString(_0) {
return __async(this, arguments, function* (options2, ssrOptions = {}) {
if (Environment.isSSR()) {
BrowserAPIs.init();
}
const { width = 400, height = 300, scale = 1 } = ssrOptions;
const virtualEl = this._createVirtualElement(width, height);
const ssrConfig = __spreadProps(__spreadValues({}, options2), {
chart: __spreadProps(__spreadValues({}, options2.chart), {
width,
height,
// Disable interactive features for SSR
toolbar: { show: false },
animations: { enabled: false }
})
});
const chart = new ApexCharts(
/** @type {HTMLElement} */
virtualEl,
ssrConfig
);
try {
yield chart.render();
const svgString = this._extractSVGString(chart, scale);
chart.destroy();
return svgString;
} catch (error) {
chart.destroy();
throw new Error(
`SSR rendering failed: ${/** @type {any} */
error.message}`
);
}
});
}
/**
* Generate hydration-ready HTML with embedded configuration
*
* @param {Record<string, any>} options - Chart configuration
* @param {{ width?: number, height?: number, scale?: number, className?: string }} [ssrOptions] - SSR-specific options
* @returns {Promise<string>} HTML string with SVG and hydration data
*
* @example
* const html = await SSRRenderer.renderToHTML({
* series: [{ data: [30, 40, 35] }],
* chart: { type: 'bar' }
* }, {
* width: 500,
* height: 300
* });
*/
static renderToHTML(_0) {
return __async(this, arguments, function* (options2, ssrOptions = {}) {
const { className = "" } = ssrOptions;
const svgString = yield this.renderToString(options2, ssrOptions);
const dataConfig = this._encodeConfig(options2);
const wrapperClass = `apexcharts-ssr-wrapper${className ? " " + className : ""}`;
return `<div class="${wrapperClass}" data-apexcharts-hydrate data-apexcharts-config="${dataConfig}">
${svgString}
</div>`;
});
}
/**
* Create a virtual DOM element for SSR rendering
* @private
* @param {number} width
* @param {number} height
*/
static _createVirtualElement(width, height) {
if (Environment.isBrowser()) {
const el = document.createElement("div");
el.style.width = `${width}px`;
el.style.height = `${height}px`;
return el;
}
return {
_ssrWidth: width,
_ssrHeight: height,
_ssrMode: true,
nodeType: 1,
nodeName: "DIV",
children: (
/** @type {any[]} */
[]
),
style: {},
classList: {
add: () => {
},
remove: () => {
},
contains: () => false
},
/**
* @param {any} child
*/
appendChild(child) {
this.children.push(child);
},
/**
* @param {any} child
*/
removeChild(child) {
const index = this.children.indexOf(child);
if (index > -1) this.children.splice(index, 1);
},
querySelector() {
return null;
},
querySelectorAll() {
return [];
},
getElementsByClassName() {
return [];
},
getAttribute() {
return null;
},
setAttribute() {
},
removeAttribute() {
},
hasAttribute() {
return false;
},
getBoundingClientRect() {
return {
width: this._ssrWidth,
height: this._ssrHeight,
top: 0,
left: 0,
right: this._ssrWidth,
bottom: this._ssrHeight,
x: 0,
y: 0
};
},
get parentNode() {
return null;
},
get isConnected() {
return true;
},
getRootNode() {
return this;
}
};
}
/**
* Extract SVG string from rendered chart
* @private
* @param {any} chart
*/
static _extractSVGString(chart, scale = 1) {
const w = chart.w;
if (!w || !w.dom || !w.dom.Paper) {
throw new Error("Chart not properly initialized");
}
const svgNode = w.dom.Paper.node;
if (Environment.isBrowser() && svgNode instanceof SVGElement) {
const serializer = new XMLSerializer();
let svgString = serializer.serializeToString(svgNode);
if (scale !== 1) {
svgString = this._applyScale(svgString, scale);
}
return svgString;
}
if (svgNode && typeof svgNode.toString === "function") {
let svgString = svgNode.toString();
if (scale !== 1) {
svgString = this._applyScale(svgString, scale);
}
return svgString;
}
throw new Error("Unable to extract SVG string from chart");
}
/**
* Apply scale transformation to SVG string
* @private
* @param {string} svgString
* @param {number} scale
*/
static _applyScale(svgString, scale) {
const widthMatch = svgString.match(/width="([^"]+)"/);
const heightMatch = svgString.match(/height="([^"]+)"/);
if (widthMatch && heightMatch) {
const width = parseFloat(widthMatch[1]);
const height = parseFloat(heightMatch[1]);
const scaledWidth = width * scale;
const scaledHeight = height * scale;
svgString = svgString.replace(/width="[^"]+"/, `width="${scaledWidth}"`).replace(/height="[^"]+"/, `height="${scaledHeight}"`);
}
return svgString;
}
/**
* Encode configuration for client-side hydration
* @private
* @param {Record<string, any>} config
*/
static _encodeConfig(config) {
try {
const json = JSON.stringify(config);
if (typeof Buffer !== "undefined") {
return Buffer.from(json).toString("base64");
} else if (typeof btoa !== "undefined") {
return btoa(json);
}
return encodeURIComponent(json);
} catch (error) {
throw new Error(
`Failed to encode config: ${/** @type {any} */
error.message}`
);
}
}
/**
* Decode configuration from hydration data
* @private
* @param {string} encodedConfig
*/
static _decodeConfig(encodedConfig) {
try {
let json;
if (typeof Buffer !== "undefined") {
json = Buffer.from(encodedConfig, "base64").toString("utf-8");
} else if (typeof atob !== "undefined") {
json = atob(encodedConfig);
} else {
json = decodeURIComponent(encodedConfig);
}
return JSON.parse(json);
} catch (error) {
throw new Error(
`Failed to decode config: ${/** @type {any} */
error.message}`
);
}
}
}
class Hydration {
/**
* Hydrate a single server-rendered chart
*
* @param {HTMLElement} el - Container element with data-apexcharts-hydrate attribute
* @param {object} clientOptions - Optional config overrides for client-side (e.g., enable animations)
* @returns {ApexCharts} Hydrated chart instance
*
* @example
* // Hydrate with default settings
* const chart = Hydration.hydrate(document.getElementById('my-chart'));
*
* @example
* // Hydrate with custom options
* const chart = Hydration.hydrate(element, {
* chart: {
* animations: { enabled: true, speed: 800 }
* }
* });
*/
static hydrate(el, clientOptions = {}) {
if (!Environment.isBrowser()) {
throw new Error("Hydration can only be performed in browser environment");
}
if (!el) {
throw new Error("Element is required for hydration");
}
if (!el.hasAttribute("data-apexcharts-hydrate")) {
throw new Error("Element does not have data-apexcharts-hydrate attribute");
}
const configAttr = el.getAttribute("data-apexcharts-config");
if (!configAttr) {
throw new Error("Element is missing data-apexcharts-config attribute");
}
const ssrConfig = this._decodeConfig(configAttr);
const config = this._mergeConfigs(ssrConfig, clientOptions);
const ssrContent = el.innerHTML;
const chart = new ApexCharts(el, config);
const rect = el.getBoundingClientRect();
el.style.width = `${rect.width}px`;
el.style.height = `${rect.height}px`;
el.innerHTML = "";
el.removeAttribute("data-apexcharts-hydrate");
el.removeAttribute("data-apexcharts-config");
chart.render().then(() => {
el.setAttribute("data-apexcharts-hydrated", "true");
el.style.width = "";
el.style.height = "";
const event = new CustomEvent("apexcharts:hydrated", {
detail: { chart, ssrContent }
});
el.dispatchEvent(event);
}).catch((error) => {
console.error("ApexCharts hydration failed:", error);
el.innerHTML = ssrContent;
el.setAttribute("data-apexcharts-hydrate", "");
el.setAttribute("data-apexcharts-config", configAttr);
throw error;
});
return chart;
}
/**
* Auto-hydrate all server-rendered charts on the page
*
* @param {string} selector - CSS selector for containers (default: '[data-apexcharts-hydrate]')
* @param {object} clientOptions - Optional config overrides applied to all charts
* @returns {ApexCharts[]} Array of hydrated chart instances
*
* @example
* // Hydrate all charts on page load
* document.addEventListener('DOMContentLoaded', () => {
* ApexCharts.hydrateAll();
* });
*
* @example
* // Hydrate with animations enabled
* ApexCharts.hydrateAll('[data-apexcharts-hydrate]', {
* chart: { animations: { enabled: true } }
* });
*/
static hydrateAll(selector = "[data-apexcharts-hydrate]", clientOptions = {}) {
if (!Environment.isBrowser()) {
throw new Error("Hydration can only be performed in browser environment");
}
const elements = document.querySelectorAll(selector);
if (elements.length === 0) {
console.warn(`No elements found matching selector: ${selector}`);
return [];
}
const charts = [];
elements.forEach((el) => {
try {
const chart = this.hydrate(
/** @type {HTMLElement} */
el,
clientOptions
);
charts.push(chart);
} catch (error) {
console.error("Failed to hydrate element:", el, error);
}
});
return charts;
}
/**
* Check if an element has been hydrated
*
* @param {HTMLElement} el - Element to check
* @returns {boolean} True if element has been hydrated
*/
static isHydrated(el) {
if (!el) return false;
return el.hasAttribute("data-apexcharts-hydrated");
}
/**
* Decode configuration from base64-encoded data attribute
* @private
* @param {string} encodedConfig
* @returns {any}
*/
static _decodeConfig(encodedConfig) {
try {
let json;
if (typeof atob !== "undefined") {
json = atob(encodedConfig);
} else if (typeof Buffer !== "undefined") {
json = Buffer.from(encodedConfig, "base64").toString("utf-8");
} else {
json = decodeURIComponent(encodedConfig);
}
return JSON.parse(json);
} catch (error) {
throw new Error(
`Failed to decode chart config: ${/** @type {any} */
error.message}`
);
}
}
/**
* Merge SSR configuration with client-side overrides
* @private
* @param {Record<string, any>} ssrConfig
* @param {Record<string, any>} clientOptions
* @returns {any}
*/
static _mergeConfigs(ssrConfig, clientOptions) {
var _a, _b, _c;
const merged = __spreadValues(__spreadValues({}, ssrConfig), clientOptions);
if (ssrConfig.chart || clientOptions.chart) {
merged.chart = __spreadValues(__spreadValues({}, ssrConfig.chart), clientOptions.chart);
if (merged.chart.animations === void 0 || merged.chart.animations.enabled === false) {
merged.chart.animations = __spreadProps(__spreadValues({}, merged.chart.animations || {}), {
enabled: true
});
}
if (((_a = clientOptions.chart) == null ? void 0 : _a.toolbar) === void 0 && ((_c = (_b = ssrConfig.chart) == null ? void 0 : _b.toolbar) == null ? void 0 : _c.show) === false) {
merged.chart.toolbar = __spreadProps(__spreadValues({}, merged.chart.toolbar || {}), {
show: true
});
}
}
return merged;
}
}
ApexCharts.renderToString = SSRRenderer.renderToString.bind(SSRRenderer);
ApexCharts.renderToHTML = SSRRenderer.renderToHTML.bind(SSRRenderer);
ApexCharts.hydrate = Hydration.hydrate.bind(Hydration);
ApexCharts.hydrateAll = Hydration.hydrateAll.bind(Hydration);
ApexCharts.isHydrated = Hydration.isHydrated.bind(Hydration);
export {
Hydration,
SSRRenderer,
ApexCharts as default
};