UNPKG

apexcharts

Version:

A JavaScript Chart Library

1,420 lines 114 kB
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)); /*! * ApexCharts v6.6.1 * (c) 2018-2026 ApexCharts */ import * as _core from "apexcharts/core"; import _core__default from "apexcharts/core"; import { default as default2 } from "apexcharts/core"; const Graphics = _core.__apex_Graphics; const DataLabels = _core.__apex_DataLabels; const BrowserAPIs = _core.__apex_BrowserAPIs_BrowserAPIs; const Environment = _core.__apex_Environment_Environment; function lengthTransitionEnabled(w) { var _a; const anim = w.config.chart.animations; if (!anim || anim.enabled === false) return false; if (!anim.dynamicAnimation || anim.dynamicAnimation.enabled === false) { return false; } const largeThreshold = (_a = anim.largeDatasetThreshold) != null ? _a : 0; if (largeThreshold > 0 && w.globals.dataPoints > largeThreshold) return false; return !!(Environment.isBrowser() && w.globals.dataChanged && w.globals.shouldAnimate); } function datumKey(w, realIndex, j) { var _a, _b, _c, _d; if ((_a = w.axisFlags) == null ? void 0 : _a.isXNumeric) { const sx = (_c = (_b = w.seriesData) == null ? void 0 : _b.seriesX) == null ? void 0 : _c[realIndex]; if (sx && sx.length && sx[j] != null) return "x:" + sx[j]; } const lbl = (_d = w.globals.labels) == null ? void 0 : _d[j]; if (lbl != null && String(lbl) !== "") { return "c:" + (Array.isArray(lbl) ? lbl.join(" ") : String(lbl)); } return "j:" + j; } function frameDatumKey(frame, realIndex, j) { var _a, _b; if (frame.isXNumeric) { const sx = (_a = frame.seriesX) == null ? void 0 : _a[realIndex]; if (sx && sx.length && sx[j] != null) return "x:" + sx[j]; } const lbl = (_b = frame.labels) == null ? void 0 : _b[j]; if (lbl != null && String(lbl) !== "") { return "c:" + (Array.isArray(lbl) ? lbl.join(" ") : String(lbl)); } return "j:" + j; } function joinKeys(oldKeys, newKeys) { const oldIndex = /* @__PURE__ */ new Map(); oldKeys.forEach((k, i) => { if (!oldIndex.has(k)) oldIndex.set(k, i); }); const toOld = new Array(newKeys.length); const usedOld = /* @__PURE__ */ new Set(); let prev = -1; let ordered = true; let identity = oldKeys.length === newKeys.length; newKeys.forEach((k, i) => { const oi = oldIndex.has(k) && !usedOld.has(oldIndex.get(k)) ? oldIndex.get(k) : -1; toOld[i] = oi; if (oi !== -1) { usedOld.add(oi); if (oi < prev) ordered = false; prev = oi; } if (oi !== i) identity = false; }); const exits = []; for (let i = 0; i < oldKeys.length; i++) { if (!usedOld.has(i)) exits.push(i); } return { toOld, exits, ordered, changed: !identity }; } function uniquifyKeys(keys) { const seen = /* @__PURE__ */ new Map(); return keys.map((k) => { const count = seen.get(k) || 0; seen.set(k, count + 1); return count === 0 ? k : `${k}#${count}`; }); } function seriesJoin(w, realIndex, includeIdentity = false, allowReorder = false) { var _a, _b; if (!lengthTransitionEnabled(w)) return null; const frame = w.globals.prevStreamFrame; if (!frame) return null; const oldY = (_a = frame.seriesY) == null ? void 0 : _a[realIndex]; const newY = (_b = w.seriesData.series) == null ? void 0 : _b[realIndex]; if (!Array.isArray(oldY) || !Array.isArray(newY)) return null; if (!oldY.length || !newY.length) return null; const oldKeys = uniquifyKeys( oldY.map((_, j) => frameDatumKey(frame, realIndex, j)) ); const newKeys = uniquifyKeys(newY.map((_, j) => datumKey(w, realIndex, j))); const join = joinKeys(oldKeys, newKeys); if (!join.ordered && !allowReorder) return null; if (!join.changed && !includeIdentity) return null; return { join, oldKeys, newKeys }; } function firstMove(d) { const m = /^M\s*([+-]?[\d.eE]+)[\s,]+([+-]?[\d.eE]+)/.exec(d || ""); if (!m) return null; const x = parseFloat(m[1]); const y = parseFloat(m[2]); return isFinite(x) && isFinite(y) ? { x, y } : null; } function renderBarExitGhosts({ w, elSeries, record, newKeys, isHorizontal, speed }) { var _a; if (!lengthTransitionEnabled(w)) return; if (!record || !Array.isArray(record.paths) || !(elSeries == null ? void 0 : elSeries.node)) return; const newKeySet = new Set(newKeys); const exits = record.paths.filter( (p) => p && p.d && p.key != null && !newKeySet.has(p.key) ); if (!exits.length) return; const graphics = new Graphics(w); const fallbackFill = (_a = w.globals.colors) == null ? void 0 : _a[parseInt(String(record.realIndex), 10)]; exits.forEach((p) => { let fill = p.fill || fallbackFill || "#c8c8c8"; if (String(fill).indexOf("url(") === 0) fill = fallbackFill || "#c8c8c8"; const ghost = graphics.drawPath({ d: p.d, stroke: "none", strokeWidth: 0, fill, fillOpacity: 1, classes: "apexcharts-bar-ghost" }); const node = ghost.node; node.setAttribute("pointer-events", "none"); ghost.attr( "clip-path", `url(#gridRectBarMask${w.globals.cuid})` ); elSeries.node.insertBefore(node, elSeries.node.firstChild); const start = firstMove(p.d); let origin = isHorizontal ? "left center" : "center bottom"; try { const bb = node.getBBox(); if (start && bb) { if (isHorizontal) { origin = Math.abs(start.x - bb.x) <= Math.abs(start.x - (bb.x + bb.width)) ? "left center" : "right center"; } else { origin = Math.abs(start.y - (bb.y + bb.height)) <= Math.abs(start.y - bb.y) ? "center bottom" : "center top"; } } } catch (_) { } const style = node.style; style.transformBox = "fill-box"; style.transformOrigin = origin; const duration = Math.max(1, speed || 1); const startAt = performance.now(); const step = (now) => { if (w.globals.isDestroyed || !node.parentNode) return; const t = Math.max(0, Math.min(1, (now - startAt) / duration)); const eased = 1 - Math.pow(1 - t, 3); const scale = 1 - eased; style.transform = isHorizontal ? `scaleX(${scale})` : `scaleY(${scale})`; style.opacity = String(1 - eased); if (t < 1) { BrowserAPIs.requestAnimationFrame(step); } else { node.parentNode.removeChild(node); } }; BrowserAPIs.requestAnimationFrame(step); }); } 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 || this.barCtx.isPyramid)) { 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) { var _a; 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; if (this.barCtx.isPyramid) { const centerOffset = (_a = textRects.centerOffset) != null ? _a : 0; dataLabelsY = bcy + barHeight / 2 + offY - centerOffset; } else { 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"; } if (this.barCtx.isPyramid) { dataLabelsX = w.layout.gridWidth / 2 + offX; } else { 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 _a2; (_a2 = this.barCtx[sg.join(",")]) == null ? void 0 : _a2.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 - offX : strokeWidth + offX; } 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 }) { var _a, _b; 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 }); const dlCfg = w.config.dataLabels; if (((_a = dlCfg.animate) == null ? void 0 : _a.enabled) || ((_b = dlCfg.countUp) == null ? void 0 : _b.enabled)) { elDataLabelsWrap.node.setAttribute( "data:dlKey", `${i}::${datumKey(w, i, j)}` ); elDataLabelsWrap.node.setAttribute("data:dlJ", String(j)); if (typeof val === "number" && isFinite(val)) { elDataLabelsWrap.node.setAttribute("data:dlVal", String(val)); } } 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; } } const Series = _core.__apex_Series; const Fill = _core.__apex_Fill; const Utils = _core.__apex_Utils; class Helpers { /** * @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.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.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, _b, _c; 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; const sl = graphics.line(x2, y1); const closing = w.config.plotOptions.bar.borderRadiusApplication === "around" || this.arrBorderRadius[realIndex][j] === "both" ? " Z" : " z"; let pathTo = graphics.move(x1, y1) + graphics.line(x1, y2) + graphics.line(x2, y2) + sl + closing; if (this.arrBorderRadius[realIndex][j] !== "none") { pathTo = graphics.roundPathCorners( pathTo, w.config.plotOptions.bar.borderRadius ); } let pathFrom = null; const morphFrom = (_c = (_b = this.barCtx.ctx) == null ? void 0 : _b.morphTypeChange) == null ? void 0 : _c.getInitialPathFor( realIndex, j ); if (morphFrom) { pathFrom = morphFrom; } else if (w.globals.previousPaths.length > 0) { pathFrom = this.barCtx.getPreviousPath(realIndex, j, pathTo); } if (pathFrom == null) { pathFrom = graphics.move(x1, y1) + graphics.line(x1, y1) + sl + sl + sl + sl + sl + graphics.line(x1, y1) + closing; } 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 }; } /** * Build a trapezoidal funnel-stage path. Used when * `plotOptions.funnel.shape === 'trapezoid'` is active alongside `isFunnel`. * * Each stage is a 4-corner polygon whose top width matches the current * stage's value and bottom width matches the next stage's value, producing * continuous sloped sides between consecutive stages. * * For the last stage, the bottom width is configurable: * - `lastShape: 'flat'` → bottom width = top width (parallel sides) * - `lastShape: 'taper'` → bottom width = 0 (taper to a point) * * @param {{ barYPosition: number, barHeight: number, series: any[][], i: number, j: number, realIndex: number, strokeWidth: number, w: any }} opts */ getFunnelTrapezoidPaths({ barYPosition, barHeight, series, i, j, realIndex, strokeWidth, w }) { var _a, _b; const graphics = new Graphics(this.barCtx.w); const center = w.layout.gridWidth / 2; const halfWidthFor = (v) => Math.abs(v / this.barCtx.invertedYRatio) / 2; const topHalf = halfWidthFor(series[i][j]); const lastIdx = series[i].length - 1; const isLast = j === lastIdx; const lastShape = w.config.plotOptions.funnel.lastShape === "taper" ? "taper" : "flat"; let bottomHalf; if (isLast) { bottomHalf = lastShape === "taper" ? 0 : topHalf; } else { bottomHalf = halfWidthFor(series[i][j + 1]); } const strokeCenter = strokeWidth / 2; const y1 = barYPosition + strokeCenter; const y2 = barYPosition + barHeight - strokeCenter; const topLeftX = center - topHalf; const topRightX = center + topHalf; const bottomLeftX = center - bottomHalf; const bottomRightX = center + bottomHalf; const pathTo = graphics.move(topLeftX, y1) + graphics.line(topRightX, y1) + graphics.line(bottomRightX, y2) + graphics.line(bottomLeftX, y2) + " Z"; let pathFrom = null; const morphFrom = (_b = (_a = this.barCtx.ctx) == null ? void 0 : _a.morphTypeChange) == null ? void 0 : _b.getInitialPathFor( realIndex, j ); if (morphFrom) { pathFrom = morphFrom; } else if (w.globals.previousPaths.length > 0) { pathFrom = this.barCtx.getPreviousPath(realIndex, j, pathTo); } if (pathFrom == null) { pathFrom = graphics.move(center, y1) + graphics.line(center, y1) + graphics.line(center, y2) + graphics.line(center, y2) + " Z"; } return { pathTo, pathFrom, // x is the right edge of the wider (top) side — used by dataLabel // positioning helpers that expect a "right" reference. x: topRightX, x1: topLeftX, barXPosition: center }; } /** * Pre-compute the per-segment layout for a value-proportional pyramid. * * Each segment is a horizontal slice of a triangle whose apex sits at the * top of the plot area (width = 0) and whose base spans `gridWidth` at the * bottom. The vertical extent of each slice is its share of the total * series value, so areas track value contribution and segments share * edges (no gaps). The first data point is the apex, the last is the base. * * @param {any[]} seriesData - 1D array of values for a single series row * @returns {{ y: number, height: number, topHalf: number, bottomHalf: number }[]} */ computePyramidLayout(seriesData) { const w = this.w; const gridHeight = w.layout.gridHeight; const gridWidth = w.layout.gridWidth; const values = seriesData.map( /** @param {any} v */ (v) => Math.abs(Number(v) || 0) ); const total = values.reduce( /** @param {number} a @param {number} b */ (a, b) => a + b, 0 ); if (total === 0 || gridHeight <= 0) { return values.map(() => ({ y: 0, height: 0, topHalf: 0, bottomHalf: 0 })); } const halfWidth = gridWidth / 2; let cumulative = 0; const layout = []; for (let j = 0; j < values.length; j++) { const topRatio = cumulative / total; cumulative += values[j]; const bottomRatio = cumulative / total; const topY = topRatio * gridHeight; const bottomY = bottomRatio * gridHeight; layout.push({ y: topY, height: bottomY - topY, topHalf: topRatio * halfWidth, bottomHalf: bottomRatio * halfWidth }); } return layout; } /** * Build a single pyramid stage path. Geometry is precomputed by * `computePyramidLayout`; this method only renders that geometry into an * SVG path string plus a `pathFrom` for entry/morph animations. * * @param {{ barYPosition: number, barHeight: number, topHalf: number, bottomHalf: number, realIndex: number, j: number, strokeWidth: number, w: any }} opts */ getPyramidPaths({ barYPosition, barHeight, topHalf, bottomHalf, realIndex, j, strokeWidth, w }) { var _a, _b; const graphics = new Graphics(this.barCtx.w); const center = w.layout.gridWidth / 2; const strokeCenter = strokeWidth / 2; const y1 = barYPosition + strokeCenter; const y2 = barYPosition + barHeight - strokeCenter; const topLeftX = center - topHalf; const topRightX = center + topHalf; const bottomLeftX = center - bottomHalf; const bottomRightX = center + bottomHalf; const pathTo = graphics.move(topLeftX, y1) + graphics.line(topRightX, y1) + graphics.line(bottomRightX, y2) + graphics.line(bottomLeftX, y2) + " Z"; let pathFrom = null; const morphFrom = (_b = (_a = this.barCtx.ctx) == null ? void 0 : _a.morphTypeChange) == null ? void 0 : _b.getInitialPathFor( realIndex, j ); if (morphFrom) { pathFrom = morphFrom; } else if (w.globals.previousPaths.length > 0) { pathFrom = this.barCtx.getPreviousPath(realIndex, j, pathTo); } if (pathFrom == null) { pathFrom = graphics.move(center, y1) + graphics.line(center, y1) + graphics.line(center, y2) + graphics.line(center, y2) + " Z"; } return { pathTo, pathFrom, x: topRightX, x1: topLeftX, barXPosition: center }; } /** @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, _b, _c; 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; const isFunnel = this.barCtx.isFunnel; const fromX = isFunnel ? (x1 + x2) / 2 : x1; const sl = graphics.line(x1, y2); const closing = w.config.plotOptions.bar.borderRadiusApplication === "around" || this.arrBorderRadius[realIndex][j] === "both" ? " Z" : " z"; let pathTo = graphics.move(x1, y1) + graphics.line(x2, y1) + graphics.line(x2, y2) + sl + closing; if (this.arrBorderRadius[realIndex][j] !== "none") { pathTo = graphics.roundPathCorners( pathTo, w.config.plotOptions.bar.borderRadius ); } let pathFrom = null; const morphFrom = (_c = (_b = this.barCtx.ctx) == null ? void 0 : _b.morphTypeChange) == null ? void 0 : _c.getInitialPathFor( realIndex, j ); if (morphFrom) { pathFrom = morphFrom; } else if (w.globals.previousPaths.length > 0) { pathFrom = this.barCtx.getPreviousPath(realIndex, j, pathTo); } if (pathFrom == null) { const slFrom = isFunnel ? graphics.line(fromX, y2) : sl; pathFrom = graphics.move(fromX, y1) + graphics.line(fromX, y1) + slFrom + slFrom + slFrom + slFrom + slFrom + graphics.line(fromX, y1) + closing; } 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 / th