apexcharts
Version:
A JavaScript Chart Library
378 lines (377 loc) • 12.3 kB
JavaScript
/*!
* ApexCharts v5.10.6
* (c) 2018-2026 ApexCharts
*/
import * as _core from "apexcharts/core";
import _core__default from "apexcharts/core";
import { default as default2 } from "apexcharts/core";
const Animations = _core.__apex_Animations;
const Graphics = _core.__apex_Graphics;
const Fill = _core.__apex_Fill;
const Series = _core.__apex_Series;
const Utils = _core.__apex_Utils;
const DataLabels = _core.__apex_DataLabels;
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();
if (w.config.plotOptions[chartType].enableShades) {
if (this.w.config.theme.mode === "dark") {
const shadeColor = utils.shadeColor(
colorShadePercent * -1,
colorProps.color
);
color = Utils.hexToRgba(
Utils.isColorHex(shadeColor) ? shadeColor : Utils.rgb2hex(shadeColor),
w.config.fill.opacity
);
} else {
const shadeColor = utils.shadeColor(colorShadePercent, colorProps.color);
color = Utils.hexToRgba(
Utils.isColorHex(shadeColor) ? shadeColor : Utils.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;
}
}
const Filters = _core.__apex_Filters;
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.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.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.isColorHex(colorFrom) ? colorFrom : Utils.rgb2hex(colorFrom),
Utils.isColorHex(color) ? color : Utils.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
});
}
}
_core__default.use({
heatmap: HeatMap
});
export {
default2 as default
};