UNPKG

apexcharts

Version:

A JavaScript Chart Library

441 lines (440 loc) 17.6 kB
/*! * ApexCharts v5.11.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 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} "; const AxesUtils = _core.__apex_axes_AxesUtils; const Data = _core.__apex_Data; const Series = _core.__apex_Series; const Utils = _core.__apex_Utils; const Environment = _core.__apex_Environment_Environment; 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(/&nbsp;/g, "&#160;")); }); }); } /** * @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(options) { if (Environment.isSSR()) return Promise.resolve({ imgURI: "" }); return new Promise((resolve) => { const w = this.w; const scale = options ? options.scale || options.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.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.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); } } _core__default.registerFeatures({ exports: Exports }); export { default2 as default };