import Graphics from '../Graphics' import Utils from '../../utils/Utils' import AxesUtils from './AxesUtils' /** * ApexCharts YAxis Class for drawing Y-Axis. * * @module YAxis **/ export default class YAxis { constructor(ctx) { this.ctx = ctx this.w = ctx.w const w = this.w this.xaxisFontSize = w.config.xaxis.labels.style.fontSize this.axisFontFamily = w.config.xaxis.labels.style.fontFamily this.xaxisForeColors = w.config.xaxis.labels.style.colors this.isCategoryBarHorizontal = w.config.chart.type === 'bar' && w.config.plotOptions.bar.horizontal this.xAxisoffX = 0 if (w.config.xaxis.position === 'bottom') { this.xAxisoffX = w.globals.gridHeight } this.drawnLabels = [] this.axesUtils = new AxesUtils(ctx) } drawYaxis(realIndex) { let w = this.w let graphics = new Graphics(this.ctx) const yaxisStyle = w.config.yaxis[realIndex].labels.style let yaxisFontSize = yaxisStyle.fontSize let yaxisFontFamily = yaxisStyle.fontFamily let yaxisFontWeight = yaxisStyle.fontWeight let elYaxis = graphics.group({ class: 'apexcharts-yaxis', rel: realIndex, transform: 'translate(' + w.globals.translateYAxisX[realIndex] + ', 0)' }) if (this.axesUtils.isYAxisHidden(realIndex)) { return elYaxis } let elYaxisTexts = graphics.group({ class: 'apexcharts-yaxis-texts-g' }) elYaxis.add(elYaxisTexts) let tickAmount = w.globals.yAxisScale[realIndex].result.length - 1 // labelsDivider is simply svg height/number of ticks let labelsDivider = w.globals.gridHeight / tickAmount // initial label position = 0; let l = w.globals.translateY let lbFormatter = w.globals.yLabelFormatters[realIndex] let labels = w.globals.yAxisScale[realIndex].result.slice() labels = this.axesUtils.checkForReversedLabels(realIndex, labels) let firstLabel = '' if (w.config.yaxis[realIndex].labels.show) { for (let i = tickAmount; i >= 0; i--) { let val = labels[i] val = lbFormatter(val, i, w) let xPad = w.config.yaxis[realIndex].labels.padding if (w.config.yaxis[realIndex].opposite && w.config.yaxis.length !== 0) { xPad = xPad * -1 } const yColors = this.axesUtils.getYAxisForeColor( yaxisStyle.colors, realIndex ) const getForeColor = () => { return Array.isArray(yColors) ? yColors[i] : yColors } let label = graphics.drawText({ x: xPad, y: l + tickAmount / 10 + w.config.yaxis[realIndex].labels.offsetY + 1, text: val, textAnchor: w.config.yaxis[realIndex].opposite ? 'start' : 'end', fontSize: yaxisFontSize, fontFamily: yaxisFontFamily, fontWeight: yaxisFontWeight, maxWidth: w.config.yaxis[realIndex].labels.maxWidth, foreColor: getForeColor(), isPlainText: false, cssClass: 'apexcharts-yaxis-label ' + yaxisStyle.cssClass }) if (i === tickAmount) { firstLabel = label } elYaxisTexts.add(label) let elTooltipTitle = document.createElementNS(w.globals.SVGNS, 'title') elTooltipTitle.textContent = Array.isArray(val) ? val.join(' ') : val label.node.appendChild(elTooltipTitle) if (w.config.yaxis[realIndex].labels.rotate !== 0) { let firstabelRotatingCenter = graphics.rotateAroundCenter( firstLabel.node ) let labelRotatingCenter = graphics.rotateAroundCenter(label.node) label.node.setAttribute( 'transform', `rotate(${w.config.yaxis[realIndex].labels.rotate} ${firstabelRotatingCenter.x} ${labelRotatingCenter.y})` ) } l = l + labelsDivider } } if (w.config.yaxis[realIndex].title.text !== undefined) { let elYaxisTitle = graphics.group({ class: 'apexcharts-yaxis-title' }) let x = 0 if (w.config.yaxis[realIndex].opposite) { x = w.globals.translateYAxisX[realIndex] } let elYAxisTitleText = graphics.drawText({ x, y: w.globals.gridHeight / 2 + w.globals.translateY + w.config.yaxis[realIndex].title.offsetY, text: w.config.yaxis[realIndex].title.text, textAnchor: 'end', foreColor: w.config.yaxis[realIndex].title.style.color, fontSize: w.config.yaxis[realIndex].title.style.fontSize, fontWeight: w.config.yaxis[realIndex].title.style.fontWeight, fontFamily: w.config.yaxis[realIndex].title.style.fontFamily, cssClass: 'apexcharts-yaxis-title-text ' + w.config.yaxis[realIndex].title.style.cssClass }) elYaxisTitle.add(elYAxisTitleText) elYaxis.add(elYaxisTitle) } let axisBorder = w.config.yaxis[realIndex].axisBorder let x = 31 + axisBorder.offsetX if (w.config.yaxis[realIndex].opposite) { x = -31 - axisBorder.offsetX } if (axisBorder.show) { let elVerticalLine = graphics.drawLine( x, w.globals.translateY + axisBorder.offsetY - 2, x, w.globals.gridHeight + w.globals.translateY + axisBorder.offsetY + 2, axisBorder.color, 0, axisBorder.width ) elYaxis.add(elVerticalLine) } if (w.config.yaxis[realIndex].axisTicks.show) { this.axesUtils.drawYAxisTicks( x, tickAmount, axisBorder, w.config.yaxis[realIndex].axisTicks, realIndex, labelsDivider, elYaxis ) } return elYaxis } // This actually becomes horizontal axis (for bar charts) drawYaxisInversed(realIndex) { let w = this.w let graphics = new Graphics(this.ctx) let elXaxis = graphics.group({ class: 'apexcharts-xaxis apexcharts-yaxis-inversed' }) let elXaxisTexts = graphics.group({ class: 'apexcharts-xaxis-texts-g', transform: `translate(${w.globals.translateXAxisX}, ${w.globals.translateXAxisY})` }) elXaxis.add(elXaxisTexts) let tickAmount = w.globals.yAxisScale[realIndex].result.length - 1 // labelsDivider is simply svg width/number of ticks let labelsDivider = w.globals.gridWidth / tickAmount + 0.1 // initial label position; let l = labelsDivider + w.config.xaxis.labels.offsetX let lbFormatter = w.globals.xLabelFormatter let labels = w.globals.yAxisScale[realIndex].result.slice() let timescaleLabels = w.globals.timescaleLabels if (timescaleLabels.length > 0) { this.xaxisLabels = timescaleLabels.slice() labels = timescaleLabels.slice() tickAmount = labels.length } labels = this.axesUtils.checkForReversedLabels(realIndex, labels) const tl = timescaleLabels.length if (w.config.xaxis.labels.show) { for (let i = tl ? 0 : tickAmount; tl ? i < tl : i >= 0; tl ? i++ : i--) { let val = labels[i] val = lbFormatter(val, i, w) let x = w.globals.gridWidth + w.globals.padHorizontal - (l - labelsDivider + w.config.xaxis.labels.offsetX) if (timescaleLabels.length) { let label = this.axesUtils.getLabel( labels, timescaleLabels, x, i, this.drawnLabels, this.xaxisFontSize ) x = label.x val = label.text this.drawnLabels.push(label.text) if (i === 0 && w.globals.skipFirstTimelinelabel) { val = '' } if (i === labels.length - 1 && w.globals.skipLastTimelinelabel) { val = '' } } let elTick = graphics.drawText({ x, y: this.xAxisoffX + w.config.xaxis.labels.offsetY + 30 - (w.config.xaxis.position === 'top' ? w.globals.xAxisHeight + w.config.xaxis.axisTicks.height - 2 : 0), text: val, textAnchor: 'middle', foreColor: Array.isArray(this.xaxisForeColors) ? this.xaxisForeColors[realIndex] : this.xaxisForeColors, fontSize: this.xaxisFontSize, fontFamily: this.xaxisFontFamily, fontWeight: w.config.xaxis.labels.style.fontWeight, isPlainText: false, cssClass: 'apexcharts-xaxis-label ' + w.config.xaxis.labels.style.cssClass }) elXaxisTexts.add(elTick) elTick.tspan(val) let elTooltipTitle = document.createElementNS(w.globals.SVGNS, 'title') elTooltipTitle.textContent = val elTick.node.appendChild(elTooltipTitle) l = l + labelsDivider } } this.inversedYAxisTitleText(elXaxis) this.inversedYAxisBorder(elXaxis) return elXaxis } inversedYAxisBorder(parent) { const w = this.w const graphics = new Graphics(this.ctx) let axisBorder = w.config.xaxis.axisBorder if (axisBorder.show) { let lineCorrection = 0 if (w.config.chart.type === 'bar' && w.globals.isXNumeric) { lineCorrection = lineCorrection - 15 } let elHorzLine = graphics.drawLine( w.globals.padHorizontal + lineCorrection + axisBorder.offsetX, this.xAxisoffX, w.globals.gridWidth, this.xAxisoffX, axisBorder.color, 0, axisBorder.height ) parent.add(elHorzLine) } } inversedYAxisTitleText(parent) { const w = this.w const graphics = new Graphics(this.ctx) if (w.config.xaxis.title.text !== undefined) { let elYaxisTitle = graphics.group({ class: 'apexcharts-xaxis-title apexcharts-yaxis-title-inversed' }) let elYAxisTitleText = graphics.drawText({ x: w.globals.gridWidth / 2 + w.config.xaxis.title.offsetX, y: this.xAxisoffX + parseFloat(this.xaxisFontSize) + parseFloat(w.config.xaxis.title.style.fontSize) + w.config.xaxis.title.offsetY + 20, text: w.config.xaxis.title.text, textAnchor: 'middle', fontSize: w.config.xaxis.title.style.fontSize, fontFamily: w.config.xaxis.title.style.fontFamily, fontWeight: w.config.xaxis.title.style.fontWeight, foreColor: w.config.xaxis.title.style.color, cssClass: 'apexcharts-xaxis-title-text ' + w.config.xaxis.title.style.cssClass }) elYaxisTitle.add(elYAxisTitleText) parent.add(elYaxisTitle) } } yAxisTitleRotate(realIndex, yAxisOpposite) { let w = this.w let graphics = new Graphics(this.ctx) let yAxisLabelsCoord = { width: 0, height: 0 } let yAxisTitleCoord = { width: 0, height: 0 } let elYAxisLabelsWrap = w.globals.dom.baseEl.querySelector( ` .apexcharts-yaxis[rel='${realIndex}'] .apexcharts-yaxis-texts-g` ) if (elYAxisLabelsWrap !== null) { yAxisLabelsCoord = elYAxisLabelsWrap.getBoundingClientRect() } let yAxisTitle = w.globals.dom.baseEl.querySelector( `.apexcharts-yaxis[rel='${realIndex}'] .apexcharts-yaxis-title text` ) if (yAxisTitle !== null) { yAxisTitleCoord = yAxisTitle.getBoundingClientRect() } if (yAxisTitle !== null) { let x = this.xPaddingForYAxisTitle( realIndex, yAxisLabelsCoord, yAxisTitleCoord, yAxisOpposite ) yAxisTitle.setAttribute('x', x.xPos - (yAxisOpposite ? 10 : 0)) } if (yAxisTitle !== null) { let titleRotatingCenter = graphics.rotateAroundCenter(yAxisTitle) yAxisTitle.setAttribute( 'transform', `rotate(${ yAxisOpposite ? w.config.yaxis[realIndex].title.rotate * -1 : w.config.yaxis[realIndex].title.rotate } ${titleRotatingCenter.x} ${titleRotatingCenter.y})` ) } } xPaddingForYAxisTitle( realIndex, yAxisLabelsCoord, yAxisTitleCoord, yAxisOpposite ) { let w = this.w let oppositeAxisCount = 0 let x = 0 let padd = 10 if (w.config.yaxis[realIndex].title.text === undefined || realIndex < 0) { return { xPos: x, padd: 0 } } if (yAxisOpposite) { x = yAxisLabelsCoord.width + w.config.yaxis[realIndex].title.offsetX + yAxisTitleCoord.width / 2 + padd / 2 oppositeAxisCount += 1 if (oppositeAxisCount === 0) { x = x - padd / 2 } } else { x = yAxisLabelsCoord.width * -1 + w.config.yaxis[realIndex].title.offsetX + padd / 2 + yAxisTitleCoord.width / 2 if (w.globals.isBarHorizontal) { padd = 25 x = yAxisLabelsCoord.width * -1 - w.config.yaxis[realIndex].title.offsetX - padd } } return { xPos: x, padd } } // sets the x position of the y-axis by counting the labels width, title width and any offset setYAxisXPosition(yaxisLabelCoords, yTitleCoords) { let w = this.w let xLeft = 0 let xRight = 0 let leftOffsetX = 18 let rightOffsetX = 1 if (w.config.yaxis.length > 1) { this.multipleYs = true } w.config.yaxis.map((yaxe, index) => { let shouldNotDrawAxis = w.globals.ignoreYAxisIndexes.indexOf(index) > -1 || !yaxe.show || yaxe.floating || yaxisLabelCoords[index].width === 0 let axisWidth = yaxisLabelCoords[index].width + yTitleCoords[index].width if (!yaxe.opposite) { xLeft = w.globals.translateX - leftOffsetX if (!shouldNotDrawAxis) { leftOffsetX = leftOffsetX + axisWidth + 20 } w.globals.translateYAxisX[index] = xLeft + yaxe.labels.offsetX } else { if (w.globals.isBarHorizontal) { xRight = w.globals.gridWidth + w.globals.translateX - 1 w.globals.translateYAxisX[index] = xRight - yaxe.labels.offsetX } else { xRight = w.globals.gridWidth + w.globals.translateX + rightOffsetX if (!shouldNotDrawAxis) { rightOffsetX = rightOffsetX + axisWidth + 20 } w.globals.translateYAxisX[index] = xRight - yaxe.labels.offsetX + 20 } } }) } setYAxisTextAlignments() { const w = this.w let yaxis = w.globals.dom.baseEl.getElementsByClassName(`apexcharts-yaxis`) yaxis = Utils.listToArray(yaxis) yaxis.forEach((y, index) => { const yaxe = w.config.yaxis[index] // proceed only if user has specified alignment if (yaxe && yaxe.labels.align !== undefined) { const yAxisInner = w.globals.dom.baseEl.querySelector( `.apexcharts-yaxis[rel='${index}'] .apexcharts-yaxis-texts-g` ) let yAxisTexts = w.globals.dom.baseEl.querySelectorAll( `.apexcharts-yaxis[rel='${index}'] .apexcharts-yaxis-label` ) yAxisTexts = Utils.listToArray(yAxisTexts) const rect = yAxisInner.getBoundingClientRect() if (yaxe.labels.align === 'left') { yAxisTexts.forEach((label, lI) => { label.setAttribute('text-anchor', 'start') }) if (!yaxe.opposite) { yAxisInner.setAttribute('transform', `translate(-${rect.width}, 0)`) } } else if (yaxe.labels.align === 'center') { yAxisTexts.forEach((label, lI) => { label.setAttribute('text-anchor', 'middle') }) yAxisInner.setAttribute( 'transform', `translate(${(rect.width / 2) * (!yaxe.opposite ? -1 : 1)}, 0)` ) } else if (yaxe.labels.align === 'right') { yAxisTexts.forEach((label, lI) => { label.setAttribute('text-anchor', 'end') }) if (yaxe.opposite) { yAxisInner.setAttribute('transform', `translate(${rect.width}, 0)`) } } } }) } }