diff --git a/babel.config.js b/babel.config.js index f14d38bf6..df1a753ab 100644 --- a/babel.config.js +++ b/babel.config.js @@ -14,7 +14,7 @@ module.exports = { ] ], plugins: ['@babel/plugin-proposal-class-properties'], - ignore: ['build/*.js', 'dist/*.js', 'samples/**/*.js'], + ignore: ['build/*.js', 'samples/**/*.js'], env: { test: { presets: [ diff --git a/jest.config.js b/jest.config.js index 867fb7ffa..927c5e3d7 100644 --- a/jest.config.js +++ b/jest.config.js @@ -24,7 +24,7 @@ module.exports = { // collectCoverageFrom: null, // The directory where Jest should output its coverage files - coverageDirectory: "coverage", + coverageDirectory: 'coverage', // An array of regexp pattern strings used to skip coverage collection // coveragePathIgnorePatterns: [ @@ -83,7 +83,7 @@ module.exports = { // notifyMode: "always", // A preset that is used as a base for Jest's configuration - preset: "jest-puppeteer", + preset: 'jest-puppeteer', // Run tests from one or more projects // projects: null, @@ -124,7 +124,7 @@ module.exports = { // snapshotSerializers: [], // The test environment that will be used for testing - testEnvironment: "jest-environment-jsdom", + testEnvironment: 'jest-environment-jsdom', // Options that will be passed to the testEnvironment // testEnvironmentOptions: {}, @@ -133,16 +133,13 @@ module.exports = { // testLocationInResults: false, // The glob patterns Jest uses to detect test files - testMatch: [ - "**/tests/**/*.js?(x)", - "**/?(*.)+(spec|test).js?(x)" - ], + testMatch: ['**/tests/**/*.js?(x)', '**/?(*.)+(spec|test).js?(x)'], // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped testPathIgnorePatterns: [ - "/node_modules/", - "/tests/unit/data/", - "/tests/unit/utils/" + '/node_modules/', + '/tests/unit/data/', + '/tests/unit/utils/' ], // The regexp pattern Jest uses to detect test files @@ -182,4 +179,4 @@ module.exports = { // Whether to use watchman for file crawling // watchman: true, -}; +} diff --git a/src/charts/Bar.js b/src/charts/Bar.js index e045b7e9a..739a67878 100644 --- a/src/charts/Bar.js +++ b/src/charts/Bar.js @@ -108,6 +108,10 @@ class Bar { this.yaxisIndex = realIndex } + this.isReversed = + w.config.yaxis[this.yaxisIndex] && + w.config.yaxis[this.yaxisIndex].reversed + let initPositions = this.initialPositions() y = initPositions.y @@ -376,7 +380,11 @@ class Bar { barHeight = (barHeight * parseInt(this.barOptions.barHeight)) / 100 - zeroW = this.baseLineInvertedY + w.globals.padHorizontal + zeroW = + this.baseLineInvertedY + + w.globals.padHorizontal + + (this.isReversed ? w.globals.gridWidth : 0) - + (this.isReversed ? this.baseLineInvertedY * 2 : 0) y = (yDivision - barHeight * this.seriesLen) / 2 } else { @@ -408,7 +416,11 @@ class Bar { 100 } - zeroH = w.globals.gridHeight - this.baseLineY[this.yaxisIndex] + zeroH = + w.globals.gridHeight - + this.baseLineY[this.yaxisIndex] - + (this.isReversed ? w.globals.gridHeight : 0) + + (this.isReversed ? this.baseLineY[this.yaxisIndex] * 2 : 0) x = w.globals.padHorizontal + (xDivision - barWidth * this.seriesLen) / 2 } @@ -466,29 +478,22 @@ class Bar { ) { x = zeroW } else { - x = zeroW + this.series[i][j] / this.invertedYRatio - } - - let endingShapeOpts = { - barHeight, - strokeWidth, - barYPosition, - x, - zeroW + x = + zeroW + + this.series[i][j] / this.invertedYRatio - + (this.isReversed ? this.series[i][j] / this.invertedYRatio : 0) * 2 } - let endingShape = this.barEndingShape(w, endingShapeOpts, this.series, i, j) pathTo = pathTo + - graphics.line(endingShape.newX, barYPosition) + - endingShape.path + + graphics.line(x, barYPosition) + + graphics.line(x, barYPosition + barHeight - strokeWidth) + graphics.line(zeroW, barYPosition + barHeight - strokeWidth) + graphics.line(zeroW, barYPosition) pathFrom = pathFrom + graphics.line(zeroW, barYPosition) + - endingShape.ending_p_from + graphics.line(zeroW, barYPosition + barHeight - strokeWidth) + graphics.line(zeroW, barYPosition + barHeight - strokeWidth) + graphics.line(zeroW, barYPosition) @@ -565,28 +570,24 @@ class Bar { ) { y = zeroH } else { - y = zeroH - this.series[i][j] / this.yRatio[this.yaxisIndex] - } - - let endingShapeOpts = { - barWidth, - strokeWidth, - barXPosition, - y, - zeroH + y = + zeroH - + this.series[i][j] / this.yRatio[this.yaxisIndex] + + (this.isReversed + ? this.series[i][j] / this.yRatio[this.yaxisIndex] + : 0) * + 2 } - let endingShape = this.barEndingShape(w, endingShapeOpts, this.series, i, j) pathTo = pathTo + - graphics.line(barXPosition, endingShape.newY) + - endingShape.path + + graphics.line(barXPosition, y) + + graphics.line(barXPosition + barWidth - strokeWidth, y) + graphics.line(barXPosition + barWidth - strokeWidth, zeroH) + graphics.line(barXPosition, zeroH) pathFrom = pathFrom + graphics.line(barXPosition, zeroH) + - endingShape.ending_p_from + graphics.line(barXPosition + barWidth - strokeWidth, zeroH) + graphics.line(barXPosition + barWidth - strokeWidth, zeroH) + graphics.line(barXPosition, zeroH) @@ -705,6 +706,7 @@ class Bar { y, i, j, + renderedPath, bcy, barHeight, textRects, @@ -721,6 +723,7 @@ class Bar { y, i, j, + renderedPath, realIndex, bcx, bcy, @@ -783,43 +786,31 @@ class Bar { } else { dataLabelsX = bcx - dataPointsDividedWidth + barWidth / 2 + offX } + let valIsNegative = this.series[i][j] <= 0 - let baseline = w.globals.gridHeight - this.baseLineY[this.yaxisIndex] - let valIsNegative = !!( - y > baseline && Math.abs(this.baseLineY[this.yaxisIndex]) !== 0 - ) - let negValuesPresent = Math.abs(w.globals.minYArr[realIndex]) !== 0 + if (w.config.yaxis[this.yaxisIndex].reversed) { + y = y - barHeight + } switch (barDataLabelsConfig.position) { case 'center': - dataLabelsY = y + barHeight / 2 + textRects.height / 2 - offY - if (negValuesPresent) { - if (valIsNegative) { - dataLabelsY = y + barHeight / 2 + textRects.height / 2 + offY - } else { - dataLabelsY = y + barHeight / 2 + textRects.height / 2 - offY - } + if (valIsNegative) { + dataLabelsY = y + barHeight / 2 + textRects.height / 2 + offY + } else { + dataLabelsY = y + barHeight / 2 + textRects.height / 2 - offY } break case 'bottom': - if (negValuesPresent) { - if (valIsNegative) { - dataLabelsY = y + barHeight + textRects.height + strokeWidth + offY - } else { - dataLabelsY = - y + barHeight - textRects.height / 2 + strokeWidth - offY - } + if (valIsNegative) { + dataLabelsY = y + barHeight + textRects.height + strokeWidth + offY } else { - dataLabelsY = w.globals.gridHeight - textRects.height / 2 - offY + dataLabelsY = + y + barHeight - textRects.height / 2 + strokeWidth - offY } break case 'top': - if (negValuesPresent) { - if (valIsNegative) { - dataLabelsY = y - textRects.height / 2 - offY - } else { - dataLabelsY = y + textRects.height + offY - } + if (valIsNegative) { + dataLabelsY = y - textRects.height / 2 - offY } else { dataLabelsY = y + textRects.height + offY } @@ -862,52 +853,33 @@ class Bar { let barWidth = this.series[i][j] / this.invertedYRatio let valIsNegative = this.series[i][j] <= 0 - let negValuesPresent = Math.abs(w.globals.minY) !== 0 + + if (w.config.yaxis[this.yaxisIndex].reversed) { + x = x + barWidth + } switch (barDataLabelsConfig.position) { case 'center': - dataLabelsX = x - barWidth / 2 + offX - if (negValuesPresent) { - if (valIsNegative) { - dataLabelsX = x - barWidth / 2 - offX - } else { - dataLabelsX = x - barWidth / 2 + offX - } + if (valIsNegative) { + dataLabelsX = x - barWidth / 2 - offX + } else { + dataLabelsX = x - barWidth / 2 + offX } break case 'bottom': - if (negValuesPresent) { - if (valIsNegative) { - dataLabelsX = - x - - barWidth - - strokeWidth - - Math.round(textRects.width / 2) - - offX - } else { - dataLabelsX = - x - - barWidth + - strokeWidth + - Math.round(textRects.width / 2) + - offX - } + if (valIsNegative) { + dataLabelsX = + x - barWidth - strokeWidth - Math.round(textRects.width / 2) - offX } else { dataLabelsX = x - barWidth + strokeWidth + Math.round(textRects.width / 2) + offX } break case 'top': - if (negValuesPresent) { - if (valIsNegative) { - dataLabelsX = - x - strokeWidth + Math.round(textRects.width / 2) - offX - } else { - dataLabelsX = - x - strokeWidth - Math.round(textRects.width / 2) + offX - } + if (valIsNegative) { + dataLabelsX = x - strokeWidth + Math.round(textRects.width / 2) - offX } else { - dataLabelsX = x + strokeWidth - Math.round(textRects.width / 2) + offX + dataLabelsX = x - strokeWidth - Math.round(textRects.width / 2) + offX } break } @@ -962,141 +934,6 @@ class Bar { return elDataLabelsWrap } - - /** barEndingShape draws the various shapes on top of bars/columns - * @memberof Bar - * @param {object} w - chart context - * @param {object} opts - consists several properties like barHeight/barWidth - * @param {array} series - global primary series - * @param {int} i - current iterating series's index - * @param {int} j - series's j of i - * @return {object} path - ending shape whether round/arrow - * ending_p_from - similar to pathFrom - * newY - which is calculated from existing y and new shape's top - **/ - barEndingShape(w, opts, series, i, j) { - let graphics = new Graphics(this.ctx) - - if (this.isHorizontal) { - let endingShape = null - let endingShapeFrom = '' - let x = opts.x - - if (typeof series[i][j] !== 'undefined' || series[i][j] !== null) { - let inverse = series[i][j] < 0 - let eX = opts.barHeight / 2 - opts.strokeWidth - if (inverse) eX = -opts.barHeight / 2 - opts.strokeWidth - - if (!w.config.chart.stacked) { - // if (Math.abs(series[i][j] / this.invertedYRatio) > eX) { - if (this.barOptions.endingShape === 'arrow') { - x = opts.x - eX - } else if (this.barOptions.endingShape === 'rounded') { - x = opts.x - eX / 2 - } - // } - } - - switch (this.barOptions.endingShape) { - case 'flat': - endingShape = graphics.line( - x, - opts.barYPosition + opts.barHeight - opts.strokeWidth - ) - break - case 'arrow': - endingShape = - graphics.line( - x + eX, - opts.barYPosition + (opts.barHeight - opts.strokeWidth) / 2 - ) + - graphics.line( - x, - opts.barYPosition + opts.barHeight - opts.strokeWidth - ) - - endingShapeFrom = graphics.line( - opts.zeroW, - opts.barYPosition + opts.barHeight - opts.strokeWidth - ) - break - case 'rounded': - endingShape = graphics.quadraticCurve( - x + eX, - opts.barYPosition + (opts.barHeight - opts.strokeWidth) / 2, - x, - opts.barYPosition + opts.barHeight - opts.strokeWidth - ) - break - } - } - return { - path: endingShape, - ending_p_from: endingShapeFrom, - newX: x - } - } else { - let endingShape = null - let endingShapeFrom = '' - let y = opts.y - - if (typeof series[i][j] !== 'undefined' || series[i][j] !== null) { - let inverse = series[i][j] < 0 - - let eY = opts.barWidth / 2 - opts.strokeWidth - - if (inverse) eY = -opts.barWidth / 2 - opts.strokeWidth - - if (!w.config.chart.stacked) { - // if (Math.abs(series[i][j] / this.yRatio[this.yaxisIndex]) > eY) { - // the arrow exceeds the chart height, hence reduce y - if (this.barOptions.endingShape === 'arrow') { - y = y + eY - } else if (this.barOptions.endingShape === 'rounded') { - y = y + eY / 2 - } - // } - } - - switch (this.barOptions.endingShape) { - case 'flat': - endingShape = graphics.line( - opts.barXPosition + opts.barWidth - opts.strokeWidth, - y - ) - break - case 'arrow': - endingShape = - graphics.line( - opts.barXPosition + (opts.barWidth - opts.strokeWidth) / 2, - y - eY - ) + - graphics.line( - opts.barXPosition + opts.barWidth - opts.strokeWidth, - y - ) - endingShapeFrom = graphics.line( - opts.barXPosition + opts.barWidth - opts.strokeWidth, - opts.zeroH - ) - break - case 'rounded': - endingShape = graphics.quadraticCurve( - opts.barXPosition + (opts.barWidth - opts.strokeWidth) / 2, - y - eY, - opts.barXPosition + opts.barWidth - opts.strokeWidth, - y - ) - break - } - } - return { - path: endingShape, - ending_p_from: endingShapeFrom, - newY: y - } - } - } } export default Bar diff --git a/src/charts/BarStacked.js b/src/charts/BarStacked.js index c36057fc8..e0d7bb4ed 100644 --- a/src/charts/BarStacked.js +++ b/src/charts/BarStacked.js @@ -55,11 +55,6 @@ class BarStacked extends Bar { } } - this.zeroSerieses = [] - this.endingShapeOnSeriesNumber = series.length - 1 // which series to draw ending shape on - - this.checkZeroSeries({ series }) - let ret = this.graphics.group({ class: 'apexcharts-bar-series apexcharts-plot-series' }) @@ -85,6 +80,10 @@ class BarStacked extends Bar { this.yaxisIndex = realIndex } + this.isReversed = + w.config.yaxis[this.yaxisIndex] && + w.config.yaxis[this.yaxisIndex].reversed + // el to which series will be drawn let elSeries = this.graphics.group({ class: `apexcharts-series ${Utils.escapeString( @@ -249,7 +248,11 @@ class BarStacked extends Bar { barHeight = (barHeight * parseInt(w.config.plotOptions.bar.barHeight)) / 100 - zeroW = this.baseLineInvertedY + w.globals.padHorizontal + zeroW = + this.baseLineInvertedY + + w.globals.padHorizontal + + (this.isReversed ? w.globals.gridWidth : 0) - + (this.isReversed ? this.baseLineInvertedY * 2 : 0) // initial y position is half of barHeight * half of number of Bars y = (yDivision - barHeight) / 2 @@ -268,7 +271,10 @@ class BarStacked extends Bar { (barWidth * parseInt(w.config.plotOptions.bar.columnWidth)) / 100 } - zeroH = this.baseLineY[this.yaxisIndex] + 1 + zeroH = + this.baseLineY[this.yaxisIndex] + + (this.isReversed ? w.globals.gridHeight : 0) - + (this.isReversed ? this.baseLineY[this.yaxisIndex] * 2 : 0) // initial x position is one third of barWidth x = w.globals.padHorizontal + (xDivision - barWidth) / 2 @@ -315,7 +321,10 @@ class BarStacked extends Bar { if (this.prevXVal[i - 1][j] < 0) { if (this.series[i][j] >= 0) { - bXP = this.prevX[i - 1][j] + prevBarW + bXP = + this.prevX[i - 1][j] + + prevBarW - + (this.isReversed ? prevBarW : 0) * 2 } else { bXP = this.prevX[i - 1][j] } @@ -323,7 +332,10 @@ class BarStacked extends Bar { if (this.series[i][j] >= 0) { bXP = this.prevX[i - 1][j] } else { - bXP = this.prevX[i - 1][j] - prevBarW + bXP = + this.prevX[i - 1][j] - + prevBarW + + (this.isReversed ? prevBarW : 0) * 2 } } @@ -336,34 +348,14 @@ class BarStacked extends Bar { if (this.series[i][j] === null) { x = barXPosition } else { - x = barXPosition + this.series[i][j] / this.invertedYRatio + x = + barXPosition + + this.series[i][j] / this.invertedYRatio - + (this.isReversed ? this.series[i][j] / this.invertedYRatio : 0) * 2 } - let endingShapeOpts = { - barHeight, - strokeWidth, - invertedYRatio: this.invertedYRatio, - barYPosition, - x - } - let endingShape = this.bar.barEndingShape( - w, - endingShapeOpts, - this.series, - i, - j - ) - - if (this.series.length > 1 && i !== this.endingShapeOnSeriesNumber) { - // revert back to flat shape if not last series - endingShape.path = this.graphics.line( - endingShape.newX, - barYPosition + barHeight - strokeWidth - ) - } - - this.xArrj.push(endingShape.newX) - this.xArrjF.push(Math.abs(barXPosition - endingShape.newX)) + this.xArrj.push(x) + this.xArrjF.push(Math.abs(barXPosition - x)) this.xArrjVal.push(this.series[i][j]) pathTo = this.graphics.move(barXPosition, barYPosition) @@ -375,8 +367,8 @@ class BarStacked extends Bar { pathTo = pathTo + - this.graphics.line(endingShape.newX, barYPosition) + - endingShape.path + + this.graphics.line(x, barYPosition) + + this.graphics.line(x, barYPosition + barHeight - strokeWidth) + this.graphics.line(barXPosition, barYPosition + barHeight - strokeWidth) + this.graphics.line(barXPosition, barYPosition) pathFrom = @@ -462,7 +454,7 @@ class BarStacked extends Bar { if (this.prevYVal[i - 1][j] < 0) { if (this.series[i][j] >= 0) { - bYP = prevYValue - prevBarH + bYP = prevYValue - prevBarH + (this.isReversed ? prevBarH : 0) * 2 } else { bYP = prevYValue } @@ -470,7 +462,7 @@ class BarStacked extends Bar { if (this.series[i][j] >= 0) { bYP = prevYValue } else { - bYP = prevYValue + prevBarH + bYP = prevYValue + prevBarH - (this.isReversed ? prevBarH : 0) * 2 } } @@ -480,38 +472,14 @@ class BarStacked extends Bar { barYPosition = w.globals.gridHeight - zeroH } - if (this.series[i][j] === null) { - y = barYPosition - this.series[i][j] / this.yRatio[this.yaxisIndex] - } else { - y = barYPosition - this.series[i][j] / this.yRatio[this.yaxisIndex] - } - - let endingShapeOpts = { - barWidth, - strokeWidth, - yRatio: this.yRatio[this.yaxisIndex], - barXPosition, - y - } - let endingShape = this.bar.barEndingShape( - w, - endingShapeOpts, - this.series, - i, - j - ) - - if (this.series.length > 1 && i !== this.endingShapeOnSeriesNumber) { - /* if(this.zeroSerieses) {} */ - // revert back to flat shape if not last series - endingShape.path = this.graphics.line( - barXPosition + barWidth - strokeWidth, - endingShape.newY - ) - } + y = + barYPosition - + this.series[i][j] / this.yRatio[this.yaxisIndex] + + (this.isReversed ? this.series[i][j] / this.yRatio[this.yaxisIndex] : 0) * + 2 - this.yArrj.push(endingShape.newY) - this.yArrjF.push(Math.abs(barYPosition - endingShape.newY)) + this.yArrj.push(y) + this.yArrjF.push(Math.abs(barYPosition - y)) this.yArrjVal.push(this.series[i][j]) pathTo = this.graphics.move(barXPosition, barYPosition) @@ -522,8 +490,8 @@ class BarStacked extends Bar { pathTo = pathTo + - this.graphics.line(barXPosition, endingShape.newY) + - endingShape.path + + this.graphics.line(barXPosition, y) + + this.graphics.line(barXPosition + barWidth - strokeWidth, y) + this.graphics.line(barXPosition + barWidth - strokeWidth, barYPosition) + this.graphics.line(barXPosition, barYPosition) pathFrom = @@ -564,39 +532,6 @@ class BarStacked extends Bar { y } } - - /* - * When user clicks on legends, the collapsed series will be filled with [0,0,0,...,0] - * We need to make sure, that the last series is not [0,0,0,...,0] - * as we need to draw shapes on the last series (for stacked bars/columns only) - * Hence, we are collecting all inner arrays in series which has [0,0,0...,0] - **/ - checkZeroSeries({ series }) { - let w = this.w - for (let zs = 0; zs < series.length; zs++) { - let total = 0 - for ( - let zsj = 0; - zsj < series[w.globals.maxValsInArrayIndex].length; - zsj++ - ) { - total += series[zs][zsj] - } - if (total === 0) { - this.zeroSerieses.push(zs) - } - } - - // After getting all zeroserieses, we need to ensure whether endingshapeonSeries is not in that zeroseries array - for (let s = series.length - 1; s >= 0; s--) { - if ( - this.zeroSerieses.indexOf(s) > -1 && - s === this.endingShapeOnSeriesNumber - ) { - this.endingShapeOnSeriesNumber -= 1 - } - } - } } export default BarStacked diff --git a/src/charts/HeatMap.js b/src/charts/HeatMap.js index 79726927c..a4ed1ebc8 100644 --- a/src/charts/HeatMap.js +++ b/src/charts/HeatMap.js @@ -38,8 +38,19 @@ export default class HeatMap { let yDivision = w.globals.gridHeight / w.globals.series.length let y1 = 0 + let rev = false - for (let i = series.length - 1; i >= 0; i--) { + let heatSeries = series.slice() + if (w.config.yaxis[0].reversed) { + rev = true + heatSeries.reverse() + } + + for ( + let i = rev ? 0 : heatSeries.length - 1; + rev ? i < heatSeries.length : i >= 0; + rev ? i++ : i-- + ) { // el to which series will be drawn let elSeries = graphics.group({ class: `apexcharts-series apexcharts-heatmap-series ${Utils.escapeString( @@ -57,7 +68,7 @@ export default class HeatMap { let x1 = 0 - for (let j = 0; j < series[i].length; j++) { + for (let j = 0; j < heatSeries[i].length; j++) { let colorShadePercent = 1 const heatColorProps = this.determineHeatColor(i, j) @@ -101,7 +112,7 @@ export default class HeatMap { i, index: i, j, - val: series[i][j], + val: heatSeries[i][j], 'stroke-width': this.strokeWidth, stroke: w.globals.stroke.colors[0], color: color @@ -156,7 +167,7 @@ export default class HeatMap { y: y1, i, j, - series, + series: heatSeries, rectHeight: yDivision, rectWidth: xDivision }) @@ -173,7 +184,13 @@ export default class HeatMap { } // adjust yaxis labels for heatmap - w.globals.yAxisScale[0].result.push('') + let yAxisScale = w.globals.yAxisScale[0].result.slice() + if (w.config.yaxis[0].reversed) { + yAxisScale.unshift('') + } else { + yAxisScale.push('') + } + w.globals.yAxisScale[0].result = yAxisScale let divisor = w.globals.gridHeight / w.globals.series.length w.config.yaxis[0].labels.offsetY = -(divisor / 2) diff --git a/src/charts/Line.js b/src/charts/Line.js index 78860f1fd..fef21a13b 100644 --- a/src/charts/Line.js +++ b/src/charts/Line.js @@ -72,11 +72,19 @@ class Line { this.yaxisIndex = realIndex } + this.isReversed = + w.config.yaxis[this.yaxisIndex] && + w.config.yaxis[this.yaxisIndex].reversed + let yArrj = [] // hold y values of current iterating series let xArrj = [] // hold x values of current iterating series // zeroY is the 0 value in y series which can be used in negative charts - let zeroY = w.globals.gridHeight - baseLineY[this.yaxisIndex] + let zeroY = + w.globals.gridHeight - + baseLineY[this.yaxisIndex] - + (this.isReversed ? w.globals.gridHeight : 0) + + (this.isReversed ? baseLineY[this.yaxisIndex] * 2 : 0) let areaBottomY = zeroY if (zeroY > w.globals.gridHeight) { @@ -214,18 +222,36 @@ class Line { typeof series[i][j + 1] === 'undefined' || series[i][j + 1] === null ) { - y = lineYPosition - minY / yRatio[this.yaxisIndex] + y = + lineYPosition - + minY / yRatio[this.yaxisIndex] + + (this.isReversed ? minY / yRatio[this.yaxisIndex] : 0) * 2 } else { - y = lineYPosition - series[i][j + 1] / yRatio[this.yaxisIndex] + y = + lineYPosition - + series[i][j + 1] / yRatio[this.yaxisIndex] + + (this.isReversed + ? series[i][j + 1] / yRatio[this.yaxisIndex] + : 0) * + 2 } } else { if ( typeof series[i][j + 1] === 'undefined' || series[i][j + 1] === null ) { - y = zeroY - minY / yRatio[this.yaxisIndex] + y = + zeroY - + minY / yRatio[this.yaxisIndex] + + (this.isReversed ? minY / yRatio[this.yaxisIndex] : 0) * 2 } else { - y = zeroY - series[i][j + 1] / yRatio[this.yaxisIndex] + y = + zeroY - + series[i][j + 1] / yRatio[this.yaxisIndex] + + (this.isReversed + ? series[i][j + 1] / yRatio[this.yaxisIndex] + : 0) * + 2 } } @@ -606,9 +632,15 @@ class Line { // the first series will not have prevY values lineYPosition = zeroY } - prevY = lineYPosition - series[i][0] / yRatio + prevY = + lineYPosition - + series[i][0] / yRatio + + (this.isReversed ? series[i][0] / yRatio : 0) * 2 } else { - prevY = zeroY - series[i][0] / yRatio + prevY = + zeroY - + series[i][0] / yRatio + + (this.isReversed ? series[i][0] / yRatio : 0) * 2 } } else { // the first value in the current series is null diff --git a/src/modules/Annotations.js b/src/modules/Annotations.js index 2ea482fdc..54593c8bf 100644 --- a/src/modules/Annotations.js +++ b/src/modules/Annotations.js @@ -153,6 +153,12 @@ export default class Annotations { w.globals.gridHeight - (anno.y - w.globals.minYArr[anno.yAxisIndex]) / (w.globals.yRange[anno.yAxisIndex] / w.globals.gridHeight) + + if (w.config.yaxis[anno.yAxisIndex].reversed) { + y1 = + (anno.y - w.globals.minYArr[anno.yAxisIndex]) / + (w.globals.yRange[anno.yAxisIndex] / w.globals.gridHeight) + } } const text = anno.label.text ? anno.label.text : '' @@ -180,6 +186,12 @@ export default class Annotations { w.globals.gridHeight - (anno.y2 - w.globals.minYArr[anno.yAxisIndex]) / (w.globals.yRange[anno.yAxisIndex] / w.globals.gridHeight) + + if (w.config.yaxis[anno.yAxisIndex].reversed) { + y2 = + (anno.y2 - w.globals.minYArr[anno.yAxisIndex]) / + (w.globals.yRange[anno.yAxisIndex] / w.globals.gridHeight) + } } if (y2 > y1) { @@ -287,6 +299,18 @@ export default class Annotations { w.globals.gridHeight - (annoY - w.globals.minYArr[anno.yAxisIndex]) / (w.globals.yRange[anno.yAxisIndex] / w.globals.gridHeight) + + if (w.config.yaxis[anno.yAxisIndex].reversed) { + y = + (annoY - w.globals.minYArr[anno.yAxisIndex]) / + (w.globals.yRange[anno.yAxisIndex] / w.globals.gridHeight) + + parseInt(anno.label.style.fontSize) + + anno.marker.size + + pointY = + (annoY - w.globals.minYArr[anno.yAxisIndex]) / + (w.globals.yRange[anno.yAxisIndex] / w.globals.gridHeight) + } } else { x = (anno.x - w.globals.minX) / (w.globals.xRange / w.globals.gridWidth) y = @@ -300,6 +324,18 @@ export default class Annotations { w.globals.gridHeight - (anno.y - w.globals.minYArr[anno.yAxisIndex]) / (w.globals.yRange[anno.yAxisIndex] / w.globals.gridHeight) + + if (w.config.yaxis[anno.yAxisIndex].reversed) { + y = + (parseFloat(anno.y) - w.globals.minYArr[anno.yAxisIndex]) / + (w.globals.yRange[anno.yAxisIndex] / w.globals.gridHeight) - + parseInt(anno.label.style.fontSize) - + anno.marker.size + + pointY = + (anno.y - w.globals.minYArr[anno.yAxisIndex]) / + (w.globals.yRange[anno.yAxisIndex] / w.globals.gridHeight) + } } if (x < 0 || x > w.globals.gridWidth) return diff --git a/src/modules/Dimensions.js b/src/modules/Dimensions.js index 4d07d42d6..a8628f241 100644 --- a/src/modules/Dimensions.js +++ b/src/modules/Dimensions.js @@ -49,9 +49,13 @@ export default class Dimensions { gl.gridHeight - w.config.grid.padding.top - w.config.grid.padding.bottom gl.gridWidth = - gl.gridWidth - w.config.grid.padding.left - w.config.grid.padding.right + gl.gridWidth - + w.config.grid.padding.left - + w.config.grid.padding.right - + this.xPadRight - + this.xPadLeft - gl.translateX = gl.translateX + w.config.grid.padding.left + gl.translateX = gl.translateX + w.config.grid.padding.left + this.xPadLeft gl.translateY = gl.translateY + w.config.grid.padding.top } @@ -59,7 +63,7 @@ export default class Dimensions { const w = this.w this.xAxisHeight = (xaxisLabelCoords.height + xtitleCoords.height) * - w.globals.lineHeightRatio + + w.globals.LINE_HEIGHT_RATIO + 15 this.xAxisWidth = xaxisLabelCoords.width @@ -163,6 +167,8 @@ export default class Dimensions { translateY = 0 } + this.additionalPaddingXLabels(xaxisLabelCoords) + switch (w.config.legend.position) { case 'bottom': gl.translateY = translateY @@ -200,9 +206,7 @@ export default class Dimensions { throw new Error('Legend position not supported') } - if (!this.isBarHorizontal) { - this.setGridXPosForDualYAxis(yTitleCoords, yaxisLabelCoords) - } + this.setGridXPosForDualYAxis(yTitleCoords, yaxisLabelCoords) // after drawing everything, set the Y axis positions let objyAxis = new YAxis(this.ctx) @@ -292,6 +296,64 @@ export default class Dimensions { }) } + // Sometimes, the last labels gets cropped in category/numeric xaxis. + // Hence, we add some additional padding based on the label length to avoid the last label being cropped. + // NOTE: datetime x-axis won't have any effect with this as we don't know the label length there due to many constraints. + additionalPaddingXLabels(xaxisLabelCoords) { + const w = this.w + this.xPadRight = 0 + this.xPadLeft = 0 + + if ( + (w.config.xaxis.type === 'category' && this.isBarHorizontal) || + w.config.xaxis.type === 'numeric' + ) { + const rightPad = (labels) => { + if (w.config.grid.padding.right < labels.width) { + this.xPadRight = labels.width / 2 + 1 + } + } + + const leftPad = (labels) => { + if (w.config.grid.padding.left < labels.width) { + this.xPadLeft = labels.width / 2 + 1 + } + } + + const lineArea = + w.config.chart.type === 'line' || w.config.chart.type === 'area' + + w.config.yaxis.forEach((yaxe, i) => { + let shouldPad = + !yaxe.show || + yaxe.floating || + w.globals.collapsedSeriesIndices.indexOf(i) !== -1 || + lineArea || + (yaxe.opposite && this.isBarHorizontal) + + if (shouldPad) { + if ( + (lineArea && + w.globals.isMultipleYAxis && + w.globals.collapsedSeriesIndices.indexOf(i) !== -1) || + (this.isBarHorizontal && yaxe.opposite) + ) { + leftPad(xaxisLabelCoords) + } + + if ( + (!this.isBarHorizontal && + yaxe.opposite && + w.globals.collapsedSeriesIndices.indexOf(i) !== -1) || + (lineArea && !w.globals.isMultipleYAxis) + ) { + rightPad(xaxisLabelCoords) + } + } + }) + } + } + titleSubtitleOffset() { const w = this.w const gl = w.globals diff --git a/src/modules/Fill.js b/src/modules/Fill.js index da0569c9b..49677457a 100644 --- a/src/modules/Fill.js +++ b/src/modules/Fill.js @@ -48,7 +48,7 @@ class Fill { imgHeight = params.height } - let elPattern = document.createElementNS(w.globals.svgNS, 'pattern') + let elPattern = document.createElementNS(w.globals.SVGNS, 'pattern') Graphics.setAttrs(elPattern, { id: params.patternID, @@ -59,7 +59,7 @@ class Fill { height: imgHeight + 'px' }) - let elImage = document.createElementNS(w.globals.svgNS, 'image') + let elImage = document.createElementNS(w.globals.SVGNS, 'image') elPattern.appendChild(elImage) elImage.setAttributeNS('http://www.w3.org/1999/xlink', 'href', fillImg) diff --git a/src/modules/Legend.js b/src/modules/Legend.js index 9ca88a0e1..031af9feb 100644 --- a/src/modules/Legend.js +++ b/src/modules/Legend.js @@ -58,7 +58,7 @@ class Legend { appendToForeignObject() { const gl = this.w.globals - var elForeign = document.createElementNS(gl.svgNS, 'foreignObject') + var elForeign = document.createElementNS(gl.SVGNS, 'foreignObject') elForeign.setAttribute('x', 0) elForeign.setAttribute('y', 0) diff --git a/src/modules/axes/Grid.js b/src/modules/axes/Grid.js index ffa691246..2e74f9419 100644 --- a/src/modules/axes/Grid.js +++ b/src/modules/axes/Grid.js @@ -108,10 +108,10 @@ class Grid { strokeSize = strokeMaxSize } - gl.dom.elGridRectMask = document.createElementNS(gl.svgNS, 'clipPath') + gl.dom.elGridRectMask = document.createElementNS(gl.SVGNS, 'clipPath') gl.dom.elGridRectMask.setAttribute('id', `gridRectMask${gl.cuid}`) - gl.dom.elGridRectMarkerMask = document.createElementNS(gl.svgNS, 'clipPath') + gl.dom.elGridRectMarkerMask = document.createElementNS(gl.SVGNS, 'clipPath') gl.dom.elGridRectMarkerMask.setAttribute( 'id', `gridRectMarkerMask${gl.cuid}` diff --git a/src/modules/axes/XAxis.js b/src/modules/axes/XAxis.js index 02a7871af..f1f103f4f 100644 --- a/src/modules/axes/XAxis.js +++ b/src/modules/axes/XAxis.js @@ -151,7 +151,7 @@ export default class XAxis { graphics.addTspan(elTick, label, this.xaxisFontFamily) - let elTooltipTitle = document.createElementNS(w.globals.svgNS, 'title') + let elTooltipTitle = document.createElementNS(w.globals.SVGNS, 'title') elTooltipTitle.textContent = label elTick.node.appendChild(elTooltipTitle) @@ -213,13 +213,18 @@ export default class XAxis { let w = this.w let graphics = new Graphics(this.ctx) + let translateYAxisX = w.config.yaxis[0].opposite + ? w.globals.translateYAxisX[realIndex] + : 0 + let elYaxis = graphics.group({ class: 'apexcharts-yaxis apexcharts-xaxis-inversed', rel: realIndex }) let elYaxisTexts = graphics.group({ - class: 'apexcharts-yaxis-texts-g apexcharts-xaxis-inversed-texts-g' + class: 'apexcharts-yaxis-texts-g apexcharts-xaxis-inversed-texts-g', + transform: 'translate(' + translateYAxisX + ', 0)' }) elYaxis.add(elYaxisTexts) @@ -251,7 +256,7 @@ export default class XAxis { x: ylabels.offsetX - 15, y: yPos + colHeight + ylabels.offsetY, text: label, - textAnchor: 'end', + textAnchor: this.yaxis.opposite ? 'start' : 'end', foreColor: ylabels.style.color ? ylabels.style.color : ylabels.style.colors[i], @@ -275,7 +280,8 @@ export default class XAxis { if (w.config.yaxis[0].title.text !== undefined) { let elXaxisTitle = graphics.group({ - class: 'apexcharts-yaxis-title apexcharts-xaxis-title-inversed' + class: 'apexcharts-yaxis-title apexcharts-xaxis-title-inversed', + transform: 'translate(' + translateYAxisX + ', 0)' }) let elXAxisTitleText = graphics.drawText({ @@ -480,7 +486,7 @@ export default class XAxis { // renderXAxisBands() { // let w = this.w; - // let plotBand = document.createElementNS(w.globals.svgNS, 'rect') + // let plotBand = document.createElementNS(w.globals.SVGNS, 'rect') // w.globals.dom.elGraphical.add(plotBand) // } } diff --git a/src/modules/axes/YAxis.js b/src/modules/axes/YAxis.js index fd05051bf..84e94f290 100644 --- a/src/modules/axes/YAxis.js +++ b/src/modules/axes/YAxis.js @@ -58,9 +58,15 @@ export default class YAxis { let l = w.globals.translateY let lbFormatter = w.globals.yLabelFormatters[realIndex] + let labels = w.globals.yAxisScale[realIndex].result.slice() + if (w.config.yaxis[realIndex].reversed) { + labels.reverse() + } + + //console.log(labels) if (w.config.yaxis[realIndex].labels.show) { for (let i = tickAmount; i >= 0; i--) { - let val = w.globals.yAxisScale[realIndex].result[i] + let val = labels[i] val = lbFormatter(val, i) @@ -182,9 +188,14 @@ export default class YAxis { let lbFormatter = w.globals.xLabelFormatter + let labels = w.globals.yAxisScale[realIndex].result.slice() + if (w.config.yaxis[realIndex].reversed) { + labels.reverse() + } + if (w.config.xaxis.labels.show) { for (let i = tickAmount; i >= 0; i--) { - let val = w.globals.yAxisScale[realIndex].result[i] + let val = labels[i] val = lbFormatter(val, i) let elTick = graphics.drawText({ @@ -208,7 +219,7 @@ export default class YAxis { elTick.tspan(val) - let elTooltipTitle = document.createElementNS(w.globals.svgNS, 'title') + let elTooltipTitle = document.createElementNS(w.globals.SVGNS, 'title') elTooltipTitle.textContent = val elTick.node.appendChild(elTooltipTitle) @@ -433,15 +444,22 @@ export default class YAxis { if (!shouldNotDrawAxis) { leftOffsetX = leftOffsetX + axisWidth + 20 } + w.globals.translateYAxisX[index] = xLeft + yaxe.labels.offsetX } else { - xRight = w.globals.gridWidth + w.globals.translateX + rightOffsetX + if (this.isBarHorizontal) { + xRight = w.globals.gridWidth + w.globals.translateX - 1 - if (!shouldNotDrawAxis) { - rightOffsetX = rightOffsetX + axisWidth + 20 - } + w.globals.translateYAxisX[index] = xRight - yaxe.labels.offsetX + } else { + xRight = w.globals.gridWidth + w.globals.translateX + rightOffsetX - w.globals.translateYAxisX[index] = xRight - yaxe.labels.offsetX + 20 + if (!shouldNotDrawAxis) { + rightOffsetX = rightOffsetX + axisWidth + 20 + } + + w.globals.translateYAxisX[index] = xRight - yaxe.labels.offsetX + 20 + } } }) } diff --git a/src/modules/settings/Config.js b/src/modules/settings/Config.js index 849a5e3c3..db5108416 100644 --- a/src/modules/settings/Config.js +++ b/src/modules/settings/Config.js @@ -102,7 +102,7 @@ export default class Config { opts.chart.type === 'scatter') && !combo.comboChartsHasBars && opts.xaxis.type !== 'datetime' && - opts.xaxis.tickPlacement === 'on' + opts.xaxis.tickPlacement !== 'between' ) { defaults.convertCatToNumeric() } @@ -247,6 +247,10 @@ export default class Config { ) } + if (config.yaxis[0].reversed) { + config.yaxis[0].opposite = true + } + config.xaxis.tooltip.enabled = false // no xaxis tooltip for horizontal bar config.yaxis[0].tooltip.enabled = false // no xaxis tooltip for horizontal bar config.chart.zoom.enabled = false // no zooming for horz bars @@ -275,6 +279,13 @@ export default class Config { } } + if (config.chart.type === 'candlestick') { + if (config.yaxis[0].reversed) { + console.warn('Reversed y-axis in candlestick chart is not supported.') + config.yaxis[0].reversed = false + } + } + if (config.chart.group && config.yaxis[0].labels.minWidth === 0) { console.warn( 'It looks like you have multiple charts in synchronization. You must provide yaxis.labels.minWidth which must be EQUAL for all grouped charts to prevent incorrect behaviour.' diff --git a/src/modules/settings/Globals.js b/src/modules/settings/Globals.js index 72b900263..1edd0706b 100644 --- a/src/modules/settings/Globals.js +++ b/src/modules/settings/Globals.js @@ -96,7 +96,7 @@ export default class Globals { xRange: 0, // xAxis range yValueDecimal: 0, // are there floating numbers in the series. If yes, this represent the len of the decimals total: 0, - svgNS: 'http://www.w3.org/2000/svg', // svg namespace + SVGNS: 'http://www.w3.org/2000/svg', // svg namespace svgWidth: 0, // the whole svg width svgHeight: 0, // the whole svg height noData: false, // whether there is any data to display or not @@ -158,7 +158,7 @@ export default class Globals { ttKeyFormatter: undefined, ttVal: undefined, ttZFormatter: undefined, - lineHeightRatio: 1.618, + LINE_HEIGHT_RATIO: 1.618, xAxisLabelsHeight: 0, yAxisLabelsWidth: 0, scaleX: 1, diff --git a/src/modules/settings/Options.js b/src/modules/settings/Options.js index 9670b367c..69d29c9db 100644 --- a/src/modules/settings/Options.js +++ b/src/modules/settings/Options.js @@ -10,6 +10,7 @@ export default class Options { showAlways: false, seriesName: undefined, opposite: false, + reversed: false, logarithmic: false, tickAmount: undefined, forceNiceScale: false, @@ -566,7 +567,7 @@ export default class Options { top: 0, right: 10, bottom: 0, - left: 10 + left: 12 } }, labels: [], @@ -794,7 +795,7 @@ export default class Options { offsetY: 0 }, tickAmount: undefined, - tickPlacement: 'between', + tickPlacement: 'on', min: undefined, max: undefined, range: undefined, diff --git a/src/modules/tooltip/Intersect.js b/src/modules/tooltip/Intersect.js index 73191e41c..129a05011 100644 --- a/src/modules/tooltip/Intersect.js +++ b/src/modules/tooltip/Intersect.js @@ -122,18 +122,20 @@ class Intersect { // let bW = 0 let i = 0 let strokeWidth + let barXY = this.getBarTooltipXY({ + e, + opt + }) + i = barXY.i + let barHeight = barXY.barHeight + let j = barXY.j if ( (ttCtx.isBarHorizontal && ttCtx.hasBars()) || !w.config.tooltip.shared ) { - let barXY = this.getBarTooltipXY({ - e, - opt - }) x = barXY.x y = barXY.y - i = barXY.i strokeWidth = Array.isArray(w.config.stroke.width) ? w.config.stroke.width[i] : w.config.stroke.width @@ -181,7 +183,22 @@ class Intersect { !ttCtx.fixedTooltip && (!w.config.tooltip.shared || (ttCtx.isBarHorizontal && ttCtx.hasBars())) ) { + if (isReversed) { + x = w.globals.gridWidth - x + } tooltipEl.style.left = x + w.globals.translateX + 'px' + + const seriesIndex = parseInt( + opt.paths.parentNode.getAttribute('data:realIndex') + ) + + const isReversed = w.globals.isMultipleYAxis + ? w.config.yaxis[seriesIndex].reversed + : w.config.yaxis[0].reversed + + if (isReversed && !(ttCtx.isBarHorizontal && ttCtx.hasBars())) { + y = y + barHeight - (w.globals.series[i][j] < 0 ? barHeight : 0) * 2 + } if (ttCtx.tooltipRect.ttHeight + y > w.globals.gridHeight) { y = w.globals.gridHeight - @@ -203,6 +220,7 @@ class Intersect { let x = 0 let y = 0 let barWidth = 0 + let barHeight = 0 const cl = e.target.classList @@ -216,6 +234,7 @@ class Intersect { let seriesBound = opt.elGrid.getBoundingClientRect() let bh = barRect.height + barHeight = barRect.height let bw = barRect.width let cx = parseInt(bar.getAttribute('cx')) @@ -289,6 +308,7 @@ class Intersect { return { x, y, + barHeight, barWidth, i, j diff --git a/src/modules/tooltip/Marker.js b/src/modules/tooltip/Marker.js index 5201afa82..e93e6a5cd 100644 --- a/src/modules/tooltip/Marker.js +++ b/src/modules/tooltip/Marker.js @@ -53,7 +53,7 @@ export default class Marker { point.node.setAttribute('default-marker-size', 0) - let elPointsG = document.createElementNS(w.globals.svgNS, 'g') + let elPointsG = document.createElementNS(w.globals.SVGNS, 'g') elPointsG.classList.add('apexcharts-series-markers') elPointsG.appendChild(point.node)