apexcharts
Version:
A JavaScript Chart Library
345 lines (308 loc) • 8.42 kB
JavaScript
// @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
}
}
}
}