apexcharts
Version:
A JavaScript Chart Library
581 lines (580 loc) • 19.1 kB
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));
/*!
* ApexCharts v7.0.0
* (c) 2018-2026 ApexCharts
*/
import * as _core from "apexcharts/core";
import _core__default from "apexcharts/core";
import { default as default2 } from "apexcharts/core";
const Utils = _core.__apex_Utils;
const TRANSFORM_KEY = "__apexcharts_series_transforms__";
if (!/** @type {any} */
globalThis[TRANSFORM_KEY]) {
globalThis[TRANSFORM_KEY] = {};
}
function getTransforms() {
return (
/** @type {any} */
globalThis[TRANSFORM_KEY]
);
}
function registerSeriesTransform(name, fn) {
if (!name || typeof name !== "string") {
console.warn(
"ApexCharts: registerSeriesTransform requires a non-empty name."
);
return;
}
if (typeof fn !== "function") {
console.warn(
`ApexCharts: registerSeriesTransform("${name}") expects a function (series, w) => series.`
);
return;
}
getTransforms()[name] = fn;
}
const ROW_SOURCE_KEY = "__apexcharts_row_sources__";
if (!/** @type {any} */
globalThis[ROW_SOURCE_KEY]) {
globalThis[ROW_SOURCE_KEY] = {};
}
function getSources() {
return (
/** @type {any} */
globalThis[ROW_SOURCE_KEY]
);
}
function registerRowSource(name, fn) {
if (!name || typeof name !== "string") {
console.warn("ApexCharts: registerRowSource requires a non-empty name.");
return;
}
if (typeof fn !== "function") {
console.warn(
`ApexCharts: registerRowSource("${name}") expects a function (w, opts) => series.`
);
return;
}
getSources()[name] = fn;
}
const MAX_BINS = 1e3;
function quantileSorted(sorted, q) {
const n = sorted.length;
if (n === 0) return NaN;
if (n === 1) return sorted[0];
const pos = (n - 1) * q;
const lo = Math.floor(pos);
const hi = Math.ceil(pos);
if (lo === hi) return sorted[lo];
return sorted[lo] + (sorted[hi] - sorted[lo]) * (pos - lo);
}
function stdDev(values) {
const n = values.length;
if (n < 2) return 0;
let sum = 0;
for (let i = 0; i < n; i++) sum += values[i];
const mean = sum / n;
let acc = 0;
for (let i = 0; i < n; i++) {
const d = values[i] - mean;
acc += d * d;
}
return Math.sqrt(acc / n);
}
function widthForRule(sorted, span, rule) {
const n = sorted.length;
const byCount = (count) => span / Math.max(1, Math.ceil(count));
switch (rule) {
case "sqrt":
return { width: byCount(Math.sqrt(n)), rule: "sqrt" };
case "rice":
return { width: byCount(2 * Math.cbrt(n)), rule: "rice" };
case "scott": {
const sd = stdDev(sorted);
if (sd > 0) return { width: 3.49 * sd * Math.pow(n, -1 / 3), rule: "scott" };
return { width: byCount(Math.log2(n) + 1), rule: "sturges" };
}
case "fd": {
const iqr = quantileSorted(sorted, 0.75) - quantileSorted(sorted, 0.25);
if (iqr > 0) return { width: 2 * iqr * Math.pow(n, -1 / 3), rule: "fd" };
return { width: byCount(Math.log2(n) + 1), rule: "sturges" };
}
case "auto": {
const sturges = byCount(Math.log2(n) + 1);
const iqr = quantileSorted(sorted, 0.75) - quantileSorted(sorted, 0.25);
if (iqr <= 0) return { width: sturges, rule: "sturges" };
const fd = 2 * iqr * Math.pow(n, -1 / 3);
return fd < sturges ? { width: fd, rule: "fd" } : { width: sturges, rule: "sturges" };
}
case "sturges":
default:
return { width: byCount(Math.log2(n) + 1), rule: "sturges" };
}
}
function computeBinning(values, opts = {}) {
if (!Array.isArray(values) || values.length === 0) return null;
const sorted = values.slice().sort((a, b) => a - b);
let lo = sorted[0];
let hi = sorted[sorted.length - 1];
const range = opts.range;
if (Array.isArray(range) && range.length === 2) {
const rLo = Number(range[0]);
const rHi = Number(range[1]);
if (isFinite(rLo) && isFinite(rHi) && rHi > rLo) {
lo = rLo;
hi = rHi;
}
}
if (!(hi > lo)) {
const pad = Math.abs(lo) > 0 ? Math.abs(lo) * 0.05 : 0.5;
return {
edges: [lo - pad, lo + pad],
binWidth: pad * 2,
rule: "single",
capped: false
};
}
const span = hi - lo;
let width;
let rule;
if (typeof opts.binWidth === "number" && opts.binWidth > 0) {
width = opts.binWidth;
rule = "binWidth";
} else if (typeof opts.bins === "number" && opts.bins >= 1) {
width = span / Math.floor(opts.bins);
rule = "count";
} else {
const chosen = widthForRule(
sorted,
span,
typeof opts.bins === "string" ? opts.bins : "auto"
);
width = chosen.width;
rule = chosen.rule;
}
if (!isFinite(width) || width <= 0) width = span;
let count = Math.ceil(span / width);
if (!isFinite(count) || count < 1) count = 1;
let capped = false;
if (count > MAX_BINS) {
count = MAX_BINS;
width = span / count;
capped = true;
}
width = span / count;
const edges = new Array(count + 1);
for (let k = 0; k <= count; k++) edges[k] = lo + k * width;
edges[count] = Math.max(edges[count], hi);
return { edges, binWidth: width, rule, capped };
}
function binIndexOf(v, edges) {
const last = edges.length - 1;
if (!(v >= edges[0]) || v > edges[last]) return -1;
if (v === edges[last]) return last - 1;
const width = (edges[last] - edges[0]) / last;
if (width > 0) {
let k = Math.floor((v - edges[0]) / width);
if (k < 0) k = 0;
if (k > last - 1) k = last - 1;
if (v < edges[k]) k--;
else if (v >= edges[k + 1]) k++;
if (k < 0 || k > last - 1) return -1;
return k;
}
let lo = 0;
let hi = last - 1;
while (lo <= hi) {
const mid = lo + hi >> 1;
if (v < edges[mid]) hi = mid - 1;
else if (v >= edges[mid + 1]) lo = mid + 1;
else return mid;
}
return -1;
}
function binCounts(values, edges) {
const counts = new Array(Math.max(0, edges.length - 1)).fill(0);
for (let i = 0; i < values.length; i++) {
const k = binIndexOf(values[i], edges);
if (k >= 0) counts[k]++;
}
return counts;
}
function rowsByBin(values, edges) {
const n = Math.max(0, edges.length - 1);
const buckets = new Array(n);
for (let k = 0; k < n; k++) buckets[k] = [];
for (let i = 0; i < values.length; i++) {
const k = binIndexOf(values[i], edges);
if (k >= 0) buckets[k].push(values[i]);
}
return buckets;
}
function fiveNumberSummary(values, opts = {}) {
if (!Array.isArray(values) || values.length === 0) return null;
const sorted = values.slice().sort((a, b) => a - b);
const q1 = quantileSorted(sorted, 0.25);
const median = quantileSorted(sorted, 0.5);
const q3 = quantileSorted(sorted, 0.75);
const iqr = q3 - q1;
let lo = sorted[0];
let hi = sorted[sorted.length - 1];
let outliers = [];
if (opts.whiskers === "tukey" && iqr > 0) {
const loFence = q1 - 1.5 * iqr;
const hiFence = q3 + 1.5 * iqr;
let i = 0;
while (i < sorted.length && sorted[i] < loFence) i++;
let j = sorted.length - 1;
while (j >= 0 && sorted[j] > hiFence) j--;
if (i <= j) {
lo = sorted[i];
hi = sorted[j];
outliers = sorted.slice(0, i).concat(sorted.slice(j + 1));
}
}
return { summary: [lo, q1, median, q3, hi], outliers, iqr };
}
function kernelDensity(values, opts = {}) {
if (!Array.isArray(values) || values.length === 0) return null;
const sorted = values.slice().sort((a, b) => a - b);
const n = sorted.length;
let h = opts.bandwidth;
if (!(typeof h === "number" && h > 0)) {
const sd = stdDev(sorted);
const iqr = quantileSorted(sorted, 0.75) - quantileSorted(sorted, 0.25);
const spread = iqr > 0 ? Math.min(sd, iqr / 1.349) : sd;
h = 0.9 * spread * Math.pow(n, -1 / 5);
}
if (!isFinite(h) || h <= 0) {
const v = sorted[0];
const eps = Math.abs(v) > 0 ? Math.abs(v) * 1e-3 : 1e-3;
return {
density: [
[v - eps, 0],
[v, 1],
[v + eps, 0]
],
bandwidth: eps
};
}
const steps = Math.max(8, Math.floor(opts.resolution || 64));
const lo = sorted[0] - 2 * h;
const hi = sorted[n - 1] + 2 * h;
const step = (hi - lo) / (steps - 1);
const norm = 1 / (n * h * Math.sqrt(2 * Math.PI));
const density = [];
for (let g = 0; g < steps; g++) {
const x = lo + g * step;
let sum = 0;
for (let i = 0; i < n; i++) {
const z = (x - sorted[i]) / h;
sum += Math.exp(-0.5 * z * z);
}
density.push([x, sum * norm]);
}
return { density, bandwidth: h };
}
function normalizeCounts(counts, opts = {}) {
let out = counts.slice();
if (opts.cumulative) {
let acc = 0;
out = out.map((c) => acc += c);
}
const total = counts.reduce((a, b) => a + b, 0);
if (total <= 0) return out;
if (opts.normalize === "relative") {
return out.map((c) => c / total * 100);
}
if (opts.normalize === "density") {
const w = opts.binWidth;
if (typeof w === "number" && w > 0) return out.map((c) => c / (total * w));
}
return out;
}
function histogramValues(data) {
const out = [];
if (!Array.isArray(data)) return out;
for (let i = 0; i < data.length; i++) {
const d = data[i];
let raw = d;
if (Array.isArray(d)) raw = d.length === 1 ? d[0] : d[1];
else if (d && typeof d === "object") raw = d.y !== void 0 ? d.y : d.x;
const v = Utils.parseNumber(raw);
if (v !== null && isFinite(v)) out.push(v);
}
return out;
}
function histogramTransform(ser, w) {
var _a;
const cnf = w.config;
const gl = w.globals;
if (!Array.isArray(ser)) return ser;
if (!gl.histogramRawSeries) {
gl.histogramRawSeries = ser.map((s) => __spreadProps(__spreadValues({}, s), {
data: Array.isArray(s == null ? void 0 : s.data) ? s.data.slice() : s == null ? void 0 : s.data
}));
}
const raw = gl.histogramRawSeries;
const hcfg = ((_a = cnf.plotOptions) == null ? void 0 : _a.histogram) || {};
const perSeries = raw.map((s) => histogramValues(s == null ? void 0 : s.data));
let all = [];
if (perSeries.length === 1) {
all = perSeries[0];
} else {
for (const vals of perSeries) all = all.concat(vals);
}
const binning = computeBinning(all, {
bins: hcfg.bins,
binWidth: hcfg.binWidth,
range: hcfg.range
});
if (!binning) {
w.histogramData = {
edges: [],
binWidth: 0,
counts: [],
rule: "",
capped: false
};
return raw;
}
const { edges, binWidth } = binning;
const counts = perSeries.map(
(vals) => binCounts(vals, edges)
);
w.histogramData = {
edges,
binWidth,
counts,
rule: binning.rule,
capped: binning.capped
};
const collapsed = gl.collapsedSeriesIndices || [];
return raw.map((s, i) => {
if (collapsed.indexOf(i) !== -1) return __spreadProps(__spreadValues({}, s), { data: [] });
const ys = normalizeCounts(counts[i], {
normalize: hcfg.normalize,
cumulative: hcfg.cumulative,
binWidth
});
const data = [];
for (let k = 0; k < ys.length; k++) {
data.push({ x: (edges[k] + edges[k + 1]) / 2, y: ys[k] });
}
return __spreadProps(__spreadValues({}, s), { data });
});
}
const derivedData = /* @__PURE__ */ new WeakSet();
function observationsOf(d, allowFlatY) {
var _a;
if (!d || typeof d !== "object" || Array.isArray(d)) return null;
let raw = null;
if (Array.isArray(d.points)) raw = d.points;
else if (Array.isArray((_a = d.y) == null ? void 0 : _a.points)) raw = d.y.points;
else if (allowFlatY && Array.isArray(d.y) && typeof d.y[0] === "number") {
raw = d.y;
}
if (!raw) return null;
const out = [];
for (let i = 0; i < raw.length; i++) {
const v = Utils.parseNumber(raw[i]);
if (v !== null && isFinite(v)) out.push(v);
}
return out.length ? out : null;
}
function boxPlotTransform(ser, w) {
var _a, _b;
if (!Array.isArray(ser)) return ser;
const whiskers = ((_b = (_a = w.config.plotOptions) == null ? void 0 : _a.boxPlot) == null ? void 0 : _b.whiskers) || "minmax";
return ser.map((s) => {
if (!Array.isArray(s == null ? void 0 : s.data)) return s;
let touched = false;
const data = s.data.map((d) => {
if (Array.isArray(d == null ? void 0 : d.y) && d.y.length === 5 && !derivedData.has(d)) {
return d;
}
const values = observationsOf(d, false);
if (!values) return d;
const summary = fiveNumberSummary(values, { whiskers });
if (!summary) return d;
touched = true;
const next = __spreadProps(__spreadValues({}, d), { y: summary.summary, points: values });
derivedData.add(next);
return next;
});
return touched ? __spreadProps(__spreadValues({}, s), { data }) : s;
});
}
function violinTransform(ser, w) {
var _a, _b;
if (!Array.isArray(ser)) return ser;
const kde = ((_b = (_a = w.config.plotOptions) == null ? void 0 : _a.violin) == null ? void 0 : _b.kde) || {};
return ser.map((s) => {
if (!Array.isArray(s == null ? void 0 : s.data)) return s;
let touched = false;
const data = s.data.map((d) => {
var _a2;
if (Array.isArray((_a2 = d == null ? void 0 : d.y) == null ? void 0 : _a2.density) && d.y.density.length && !derivedData.has(d)) {
return d;
}
const values = observationsOf(d, true);
if (!values) return d;
const est = kernelDensity(values, {
bandwidth: kde.bandwidth,
resolution: kde.resolution
});
if (!est) return d;
touched = true;
const next = __spreadProps(__spreadValues({}, d), { y: { density: est.density, points: values } });
derivedData.add(next);
return next;
});
return touched ? __spreadProps(__spreadValues({}, s), { data }) : s;
});
}
const DEFAULT_MAX_ROWS = 3e3;
function thinClusters(clusters, maxRows) {
let total = 0;
let widest = 0;
for (const c of clusters) {
total += c.length;
if (c.length > widest) widest = c.length;
}
if (total <= maxRows) return { clusters, stride: 1, total, kept: total };
const keptAt = (s) => {
let n = 0;
for (const c of clusters) n += Math.ceil(c.length / s);
return n;
};
let stride = Math.max(2, Math.ceil(total / maxRows));
while (stride < widest && keptAt(stride) > maxRows) stride++;
let kept = 0;
const out = clusters.map((rows) => {
const keepList = [];
for (let i = 0; i < rows.length; i += stride) keepList.push(rows[i]);
kept += keepList.length;
return keepList;
});
return { clusters: out, stride, total, kept };
}
function toUnitSeries(w, clusters, opts) {
const maxRows = opts && opts.maxRows != null ? opts.maxRows : DEFAULT_MAX_ROWS;
const thinned = thinClusters(
clusters.map((c) => c.rows),
maxRows
);
if (thinned.stride > 1) {
console.warn(
`ApexCharts: rowSeries() thinned ${thinned.total} rows to ${thinned.kept} (every ${thinned.stride}${thinned.stride === 2 ? "nd" : thinned.stride === 3 ? "rd" : "th"} row) to stay under maxRows=${maxRows}. Raise maxRows to draw more.`
);
}
const colors = w.globals && w.globals.colors || [];
return clusters.map((c, i) => {
const fillColor = colors[c.realIndex] || colors[0];
return {
name: c.name,
data: thinned.clusters[i].map((v, q) => __spreadValues({
id: `${c.realIndex}:${i}:${q}`,
x: c.name,
y: v
}, fillColor ? { fillColor } : {}))
};
});
}
function histogramRows(w, opts) {
const gl = w.globals;
const hd = w.histogramData;
const raw = gl && gl.histogramRawSeries;
if (!hd || !Array.isArray(hd.edges) || hd.edges.length < 2) return null;
if (!Array.isArray(raw) || !raw.length) return null;
const collapsed = gl && gl.collapsedSeriesIndices || [];
const edges = hd.edges;
const clusters = [];
raw.forEach((s, i) => {
var _a;
if (collapsed.indexOf(i) !== -1) return;
const buckets = rowsByBin(histogramValues(s && s.data), edges);
const seriesName = w.seriesData && ((_a = w.seriesData.seriesNames) == null ? void 0 : _a[i]) || (s == null ? void 0 : s.name);
buckets.forEach((rows, k) => {
const range = `${formatEdge(edges[k])}-${formatEdge(edges[k + 1])}`;
clusters.push({
// Only qualify by series when there is more than one to tell apart.
name: raw.length > 1 && seriesName ? `${seriesName} ${range}` : range,
realIndex: i,
rows
});
});
});
return clusters.length ? toUnitSeries(w, clusters, opts) : null;
}
function formatEdge(v) {
if (!isFinite(v)) return String(v);
const r = Math.round(v);
return Math.abs(v - r) < 1e-6 ? String(r) : String(Number(v.toFixed(2)));
}
function pointsRowSource(pick) {
return (w, opts) => {
var _a;
const perSeries = pick(w);
if (!Array.isArray(perSeries) || !perSeries.length) return null;
const collapsed = w.globals && w.globals.collapsedSeriesIndices || [];
const labels = w.globals && (((_a = w.globals.categoryLabels) == null ? void 0 : _a.length) ? w.globals.categoryLabels : w.globals.labels) || [];
const clusters = [];
perSeries.forEach((byCat, i) => {
var _a2;
if (collapsed.indexOf(i) !== -1) return;
if (!Array.isArray(byCat)) return;
const seriesName = w.seriesData && ((_a2 = w.seriesData.seriesNames) == null ? void 0 : _a2[i]);
byCat.forEach((pts, j) => {
const label = labels[j] != null ? String(labels[j]) : `#${j + 1}`;
clusters.push({
name: perSeries.length > 1 && seriesName ? `${seriesName} ${label}` : label,
realIndex: i,
rows: Array.isArray(pts) ? pts.slice() : []
});
});
});
return clusters.length ? toUnitSeries(w, clusters, opts) : null;
};
}
const boxPlotRows = pointsRowSource((w) => {
var _a;
return (_a = w.candleData) == null ? void 0 : _a.seriesBoxPoints;
});
const violinRows = pointsRowSource((w) => {
var _a;
return (_a = w.violinData) == null ? void 0 : _a.seriesViolinPoints;
});
registerSeriesTransform("histogram", histogramTransform);
registerSeriesTransform("boxPlot", boxPlotTransform);
registerSeriesTransform("violin", violinTransform);
registerRowSource("histogram", histogramRows);
registerRowSource("boxPlot", boxPlotRows);
registerRowSource("violin", violinRows);
export {
boxPlotRows,
boxPlotTransform,
default2 as default,
histogramRows,
histogramTransform,
violinRows,
violinTransform
};