UNPKG

apexcharts

Version:

A JavaScript Chart Library

345 lines (308 loc) 8.42 kB
// @ts-check import Formatters from '../Formatters' import Graphics from '../Graphics' import CoreUtils from '../CoreUtils' import DateTime from '../../utils/DateTime' export default class AxesUtils { /** * @param {import('../../types/internal').ChartStateW} w */ constructor(w, { theme = null, timeScale = null } = {}) { this.w = w /** @type {any} */ this.theme = theme /** @type {any} */ 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 !== undefined) { label = customFormatter(rawLabel, labels[i], { i, dateFormatter: new DateTime(this.w).formatDate, w, }) } } /** * @param {string} unit */ const determineHighestUnit = (unit) => { let highestUnit = null /** * @param {Record<string, any>} t */ timescaleLabels.forEach((/** @type {any} */ 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 === undefined) { 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 { /** @type {any} */ ;(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) { // check if first label is being truncated if (w.globals.skipFirstTimelinelabel) { /** @type {any} */ ;(label).text = '' } } if (i === labelsLen - 1) { // check if last label is being truncated if (w.globals.skipLastTimelinelabel) { /** @type {any} */ ;(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 ) { /** @type {any} */ ;(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 /** * @param {number} si */ 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) /** * @param {number} si */ 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) { const w = this.w if (Array.isArray(yColors) && w.globals.yAxisScale[realIndex]) { this.theme?.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) // initial label position = 0; 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 } } } }