apexcharts
Advanced tools
Comparing version 3.45.2 to 3.46.0
{ | ||
"name": "apexcharts", | ||
"version": "3.45.2", | ||
"version": "3.46.0", | ||
"description": "A JavaScript Chart Library", | ||
@@ -58,3 +58,3 @@ "repository": { | ||
"chalk": "3.0.0", | ||
"css-loader": "3.4.1", | ||
"css-loader": "6.10.0", | ||
"eslint": "8.36.0", | ||
@@ -61,0 +61,0 @@ "eslint-config-prettier": "8.8.0", |
@@ -176,3 +176,3 @@ import BarDataLabels from './common/bar/DataLabels' | ||
for (let j = 0; j < w.globals.dataPoints; j++) { | ||
for (let j = 0; j < series[i].length; j++) { | ||
const strokeWidth = this.barHelpers.getStrokeWidth(i, j, realIndex) | ||
@@ -179,0 +179,0 @@ |
@@ -735,5 +735,3 @@ import Animations from '../modules/Animations' | ||
Math.ceil(this.maxY), | ||
w.config.yaxis[0].tickAmount, | ||
0, | ||
true | ||
0 | ||
) | ||
@@ -740,0 +738,0 @@ |
@@ -99,5 +99,3 @@ import Formatters from '../Formatters' | ||
!Array.isArray(label) && | ||
(label.indexOf('NaN') === 0 || | ||
label.toLowerCase().indexOf('invalid') === 0 || | ||
label.toLowerCase().indexOf('infinity') >= 0 || | ||
(String(label) === 'NaN' || | ||
(drawnLabels.indexOf(label) >= 0 && allowDuplicatesInTimeScale)) | ||
@@ -104,0 +102,0 @@ ) { |
@@ -638,26 +638,3 @@ import Bar from '../charts/Bar' | ||
let targetChart = ApexCharts.getChartByID(target) | ||
let yaxis = Utils.clone(w.config.yaxis) | ||
if ( | ||
w.config.chart.brush.autoScaleYaxis && | ||
targetChart.w.globals.series.length === 1 | ||
) { | ||
const scale = new Scales(targetChart) | ||
yaxis = scale.autoScaleY(targetChart, yaxis, e) | ||
} | ||
const multipleYaxis = targetChart.w.config.yaxis.reduce( | ||
(acc, curr, index) => { | ||
return [ | ||
...acc, | ||
{ | ||
...targetChart.w.config.yaxis[index], | ||
min: yaxis[0].min, | ||
max: yaxis[0].max, | ||
}, | ||
] | ||
}, | ||
[] | ||
) | ||
targetChart.ctx.updateHelpers._updateOptions( | ||
@@ -668,4 +645,3 @@ { | ||
max: e.xaxis.max, | ||
}, | ||
yaxis: multipleYaxis, | ||
} | ||
}, | ||
@@ -672,0 +648,0 @@ false, |
@@ -68,6 +68,12 @@ import AxesUtils from '../axes/AxesUtils' | ||
} | ||
// Here, barWidth is assumed to be the width occupied by a group of bars. | ||
// There will be one bar in the group for each series plotted. | ||
// Note: This version of the following math is different to that over in | ||
// Helpers.js. Don't assume they should be the same. Over there, | ||
// xDivision is computed differently and it's used on different charts. | ||
// They were the same, but the solution to | ||
// https://github.com/apexcharts/apexcharts.js/issues/4178 | ||
// was to remove the division by seriesLen. | ||
barWidth = | ||
((xDivision / seriesLen) * | ||
parseInt(w.config.plotOptions.bar.columnWidth, 10)) / | ||
100 | ||
(xDivision * parseInt(w.config.plotOptions.bar.columnWidth, 10)) / 100 | ||
@@ -78,4 +84,2 @@ if (barWidth < 1) { | ||
barWidth = barWidth / (seriesLen > 1 ? 1 : 1.5) + 5 | ||
w.globals.barPadForNumericAxis = barWidth | ||
@@ -82,0 +86,0 @@ } |
@@ -66,3 +66,3 @@ import Utils from './../utils/Utils' | ||
filter.componentTransfer({ | ||
rgb: { type: 'linear', slope: 1.5, intercept: intensity } | ||
rgb: { type: 'linear', slope: 1.5, intercept: intensity }, | ||
}) | ||
@@ -92,3 +92,3 @@ }) | ||
filter.componentTransfer({ | ||
rgb: { type: 'linear', slope: intensity } | ||
rgb: { type: 'linear', slope: intensity }, | ||
}) | ||
@@ -108,3 +108,3 @@ }) | ||
this.addLightenFilter(el, i, { | ||
intensity | ||
intensity, | ||
}) | ||
@@ -115,3 +115,3 @@ break | ||
this.addDarkenFilter(el, i, { | ||
intensity | ||
intensity, | ||
}) | ||
@@ -128,4 +128,11 @@ break | ||
addShadow(add, i, attrs) { | ||
const w = this.w | ||
const { blur, top, left, color, opacity } = attrs | ||
if (w.config.chart.dropShadow.enabledOnSeries?.length > 0) { | ||
if (w.config.chart.dropShadow.enabledOnSeries.indexOf(i) === -1) { | ||
return add | ||
} | ||
} | ||
let shadowBlur = add | ||
@@ -153,2 +160,8 @@ .flood(Array.isArray(color) ? color[i] : color, opacity) | ||
if (w.config.chart.dropShadow.enabledOnSeries?.length > 0) { | ||
if (w.config.chart.dropShadow.enabledOnSeries?.indexOf(i) === -1) { | ||
return el | ||
} | ||
} | ||
color = Array.isArray(color) ? color[i] : color | ||
@@ -213,3 +226,3 @@ | ||
x: '-50%', | ||
y: '-50%' | ||
y: '-50%', | ||
}) | ||
@@ -216,0 +229,0 @@ } |
@@ -453,10 +453,4 @@ import Animations from './Animations' | ||
if (w.config.chart.dropShadow.enabled && drawShadow) { | ||
if ( | ||
!w.config.chart.dropShadow.enabledOnSeries || | ||
(w.config.chart.dropShadow.enabledOnSeries && | ||
w.config.chart.dropShadow.enabledOnSeries.indexOf(realIndex) !== -1) | ||
) { | ||
const shadow = w.config.chart.dropShadow | ||
filters.dropShadow(el, shadow, realIndex) | ||
} | ||
const shadow = w.config.chart.dropShadow | ||
filters.dropShadow(el, shadow, realIndex) | ||
} | ||
@@ -735,2 +729,89 @@ } | ||
/** | ||
* Creates a group with given attributes. | ||
* @param {number} x - The x-coordinate of the group. | ||
* @param {number} y - The y-coordinate of the group. | ||
* @param {Array} lines - The lines to be added to the group. | ||
* @param {Object} opts - The options for the group. | ||
* @returns {Object} The created group. | ||
*/ | ||
createGroupWithAttributes(x, y, lines, opts) { | ||
let elPoint = this.group() | ||
lines.forEach((line) => elPoint.add(line)) | ||
elPoint.attr({ | ||
class: opts.class ? opts.class : '', | ||
cy: y, | ||
cx: x, | ||
}) | ||
return elPoint | ||
} | ||
/** | ||
* Draws a plus sign at the given coordinates. | ||
* @param {number} x - The x-coordinate of the plus sign. | ||
* @param {number} y - The y-coordinate of the plus sign. | ||
* @param {number} size - The size of the plus sign. | ||
* @param {Object} opts - The options for the plus sign. | ||
* @returns {Object} The created plus sign. | ||
*/ | ||
drawPlus(x, y, size, opts) { | ||
let halfSize = size / 2 | ||
let line1 = this.drawLine( | ||
x, | ||
y - halfSize, | ||
x, | ||
y + halfSize, | ||
opts.pointStrokeColor, | ||
opts.pointStrokeDashArray, | ||
opts.pointStrokeWidth, | ||
opts.pointStrokeLineCap | ||
) | ||
let line2 = this.drawLine( | ||
x - halfSize, | ||
y, | ||
x + halfSize, | ||
y, | ||
opts.pointStrokeColor, | ||
opts.pointStrokeDashArray, | ||
opts.pointStrokeWidth, | ||
opts.pointStrokeLineCap | ||
) | ||
return this.createGroupWithAttributes(x, y, [line1, line2], opts) | ||
} | ||
/** | ||
* Draws an 'X' at the given coordinates. | ||
* @param {number} x - The x-coordinate of the 'X'. | ||
* @param {number} y - The y-coordinate of the 'X'. | ||
* @param {number} size - The size of the 'X'. | ||
* @param {Object} opts - The options for the 'X'. | ||
* @returns {Object} The created 'X'. | ||
*/ | ||
drawX(x, y, size, opts) { | ||
let halfSize = size / 2 | ||
let line1 = this.drawLine( | ||
x - halfSize, | ||
y - halfSize, | ||
x + halfSize, | ||
y + halfSize, | ||
opts.pointStrokeColor, | ||
opts.pointStrokeDashArray, | ||
opts.pointStrokeWidth, | ||
opts.pointStrokeLineCap | ||
) | ||
let line2 = this.drawLine( | ||
x - halfSize, | ||
y + halfSize, | ||
x + halfSize, | ||
y - halfSize, | ||
opts.pointStrokeColor, | ||
opts.pointStrokeDashArray, | ||
opts.pointStrokeWidth, | ||
opts.pointStrokeLineCap | ||
) | ||
return this.createGroupWithAttributes(x, y, [line1, line2], opts) | ||
} | ||
drawMarker(x, y, opts) { | ||
@@ -741,4 +822,7 @@ x = x || 0 | ||
let elPoint = null | ||
if (opts.shape === 'square' || opts.shape === 'rect') { | ||
if (opts?.shape === 'X' || opts?.shape === 'x') { | ||
elPoint = this.drawX(x, y, size, opts) | ||
} else if (opts?.shape === 'plus' || opts?.shape === '+') { | ||
elPoint = this.drawPlus(x, y, size, opts) | ||
} else if (opts.shape === 'square' || opts.shape === 'rect') { | ||
let radius = opts.pRadius === undefined ? size / 2 : opts.pRadius | ||
@@ -745,0 +829,0 @@ |
@@ -29,3 +29,3 @@ import Utils from '../utils/Utils' | ||
highestY = -Number.MAX_VALUE, | ||
len = null | ||
endingIndex = null | ||
) { | ||
@@ -37,6 +37,30 @@ const cnf = this.w.config | ||
if (len === null) { | ||
len = startingIndex + 1 | ||
if (endingIndex === null) { | ||
endingIndex = startingIndex + 1 | ||
} | ||
const series = gl.series | ||
let firstXIndex = 0 | ||
let lastXIndex = 0 | ||
let seriesX = undefined | ||
if (gl.seriesX.length >= endingIndex) { | ||
seriesX = [...new Set([].concat(...gl.seriesX.slice(startingIndex, endingIndex)))] | ||
firstXIndex = 0 | ||
lastXIndex = seriesX.length - 1 | ||
if (cnf.xaxis.min) { | ||
for ( | ||
firstXIndex = 0; | ||
firstXIndex < lastXIndex && seriesX[firstXIndex] <= cnf.xaxis.min; | ||
firstXIndex++ | ||
) {} | ||
} | ||
if (cnf.xaxis.max) { | ||
for ( | ||
; | ||
lastXIndex > firstXIndex && seriesX[lastXIndex] >= cnf.xaxis.max; | ||
lastXIndex-- | ||
) {} | ||
} | ||
} | ||
let series = gl.series | ||
let seriesMin = series | ||
@@ -56,3 +80,3 @@ let seriesMax = series | ||
for (let i = startingIndex; i < len; i++) { | ||
for (let i = startingIndex; i < endingIndex; i++) { | ||
gl.dataPoints = Math.max(gl.dataPoints, series[i].length) | ||
@@ -74,3 +98,7 @@ | ||
} | ||
for (let j = 0; j < gl.series[i].length; j++) { | ||
if (!seriesX) { | ||
firstXIndex = 0 | ||
lastXIndex = gl.series[i].length | ||
} | ||
for (let j = firstXIndex; j <= lastXIndex; j++) { | ||
let val = series[i][j] | ||
@@ -87,37 +115,33 @@ if (val !== null && Utils.isNumber(val)) { | ||
if ( | ||
this.w.config.chart.type === 'candlestick' || | ||
this.w.config.chart.type === 'boxPlot' || | ||
this.w.config.chart.type !== 'rangeArea' || | ||
this.w.config.chart.type !== 'rangeBar' | ||
) { | ||
if ( | ||
this.w.config.chart.type === 'candlestick' || | ||
this.w.config.chart.type === 'boxPlot' | ||
) { | ||
// These series arrays are dual purpose: | ||
// Array : CandleO, CandleH, CandleM, CandleL, CandleC | ||
// Candlestick: O H L C | ||
// Boxplot : Min Q1 Median Q3 Max | ||
switch (cnf.series[i].type) { | ||
case 'candlestick': { | ||
if (typeof gl.seriesCandleC[i][j] !== 'undefined') { | ||
maxY = Math.max(maxY, gl.seriesCandleO[i][j]) | ||
maxY = Math.max(maxY, gl.seriesCandleH[i][j]) | ||
maxY = Math.max(maxY, gl.seriesCandleL[i][j]) | ||
lowestY = Math.min(lowestY, gl.seriesCandleL[i][j]) | ||
} | ||
} | ||
case 'boxPlot': { | ||
if (typeof gl.seriesCandleC[i][j] !== 'undefined') { | ||
maxY = Math.max(maxY, gl.seriesCandleC[i][j]) | ||
if (this.w.config.chart.type === 'boxPlot') { | ||
maxY = Math.max(maxY, gl.seriesCandleM[i][j]) | ||
} | ||
lowestY = Math.min(lowestY, gl.seriesCandleO[i][j]) | ||
} | ||
} | ||
} | ||
// there is a combo chart and the specified series in not either candlestick, boxplot, or rangeArea/rangeBar; find the max there | ||
if ( | ||
cnf.series[i].type && | ||
(cnf.series[i].type !== 'candlestick' || | ||
cnf.series[i].type !== 'boxPlot' || | ||
cnf.series[i].type !== 'rangeArea' || | ||
cnf.series[i].type !== 'rangeBar') | ||
) { | ||
maxY = Math.max(maxY, gl.series[i][j]) | ||
lowestY = Math.min(lowestY, gl.series[i][j]) | ||
} | ||
highestY = maxY | ||
// there is a combo chart and the specified series in not either candlestick, boxplot, or rangeArea/rangeBar; find the max there | ||
if ( | ||
cnf.series[i].type && | ||
(cnf.series[i].type !== 'candlestick' && | ||
cnf.series[i].type !== 'boxPlot' && | ||
cnf.series[i].type !== 'rangeArea' && | ||
cnf.series[i].type !== 'rangeBar') | ||
) { | ||
maxY = Math.max(maxY, gl.series[i][j]) | ||
lowestY = Math.min(lowestY, gl.series[i][j]) | ||
} | ||
highestY = maxY | ||
@@ -191,7 +215,8 @@ if ( | ||
// we need to get minY and maxY for multiple y axis | ||
lowestYInAllSeries = Number.MAX_VALUE | ||
for (let i = 0; i < gl.series.length; i++) { | ||
const minYMaxYArr = this.getMinYMaxY(i, lowestYInAllSeries, null, i + 1) | ||
gl.minYArr.push(minYMaxYArr.minY) | ||
gl.maxYArr.push(minYMaxYArr.maxY) | ||
lowestYInAllSeries = minYMaxYArr.lowestY | ||
const minYMaxYArr = this.getMinYMaxY(i) | ||
gl.minYArr[i] = minYMaxYArr.lowestY | ||
gl.maxYArr[i] = minYMaxYArr.highestY | ||
lowestYInAllSeries = Math.min(lowestYInAllSeries, minYMaxYArr.lowestY) | ||
} | ||
@@ -207,4 +232,4 @@ } | ||
) | ||
gl.minY = minYMaxY.minY | ||
gl.maxY = minYMaxY.maxY | ||
gl.minY = minYMaxY.lowestY | ||
gl.maxY = minYMaxY.highestY | ||
lowestYInAllSeries = minYMaxY.lowestY | ||
@@ -217,6 +242,9 @@ | ||
// if the numbers are too big, reduce the range | ||
// for eg, if number is between 100000-110000, putting 0 as the lowest value is not so good idea. So change the gl.minY for line/area/candlesticks/boxPlot | ||
// for eg, if number is between 100000-110000, putting 0 as the lowest | ||
// value is not so good idea. So change the gl.minY for | ||
// line/area/scatter/candlesticks/boxPlot/vertical rangebar | ||
if ( | ||
cnf.chart.type === 'line' || | ||
cnf.chart.type === 'area' || | ||
cnf.chart.type === 'scatter' || | ||
cnf.chart.type === 'candlestick' || | ||
@@ -231,23 +259,6 @@ cnf.chart.type === 'boxPlot' || | ||
) { | ||
let diff = gl.maxY - lowestYInAllSeries | ||
if ( | ||
(lowestYInAllSeries >= 0 && lowestYInAllSeries <= 10) || | ||
cnf.yaxis[0].min !== undefined || | ||
cnf.yaxis[0].max !== undefined | ||
) { | ||
// if minY is already 0/low value, we don't want to go negatives here - so this check is essential. | ||
diff = 0 | ||
} | ||
gl.minY = lowestYInAllSeries - (diff * 5) / 100 | ||
/* fix https://github.com/apexcharts/apexcharts.js/issues/614 */ | ||
/* fix https://github.com/apexcharts/apexcharts.js/issues/968 */ | ||
if (lowestYInAllSeries > 0 && gl.minY < 0) { | ||
gl.minY = 0 | ||
} | ||
/* fix https://github.com/apexcharts/apexcharts.js/issues/426 */ | ||
gl.maxY = gl.maxY + (diff * 5) / 100 | ||
gl.minY = lowestYInAllSeries | ||
} | ||
} else { | ||
gl.minY = minYMaxY.minY | ||
} | ||
@@ -411,3 +422,3 @@ | ||
niceMin: catScale[0], | ||
niceMax: catScale[catScale.length - 1], | ||
niceMax: catScale[catScale.length - 1] | ||
} | ||
@@ -524,3 +535,3 @@ } else { | ||
gl.seriesX[gl.maxValsInArrayIndex][ | ||
gl.seriesX[gl.maxValsInArrayIndex].length - 1 | ||
gl.seriesX[gl.maxValsInArrayIndex].length - 1 | ||
] | ||
@@ -588,3 +599,3 @@ ) | ||
? (stackedPoss[group][j] += | ||
parseFloat(gl.series[i][j]) + 0.0001) | ||
parseFloat(gl.series[i][j]) + 0.0001) | ||
: (stackedNegs[group][j] += parseFloat(gl.series[i][j])) | ||
@@ -597,3 +608,3 @@ } | ||
Object.entries(stackedPoss).forEach(([key]) => { | ||
Object.entries(stackedPoss).forEach(([key]) => { | ||
stackedPoss[key].forEach((_, stgi) => { | ||
@@ -600,0 +611,0 @@ gl.maxY = Math.max(gl.maxY, stackedPoss[key][stgi]) |
import Utils from '../utils/Utils' | ||
export default class Range { | ||
export default class Scales { | ||
constructor(ctx) { | ||
@@ -9,13 +9,35 @@ this.ctx = ctx | ||
// http://stackoverflow.com/questions/326679/choosing-an-attractive-linear-scale-for-a-graphs-y-axiss | ||
// http://stackoverflow.com/questions/326679/choosing-an-attractive-linear-scale-for-a-graphs-y-axis | ||
// This routine creates the Y axis values for a graph. | ||
niceScale(yMin, yMax, ticks = 5, index = 0, NO_MIN_MAX_PROVIDED) { | ||
niceScale(yMin, yMax, index = 0) { | ||
const jsPrecision = 1e-11 // JS precision errors | ||
const w = this.w | ||
// Determine Range | ||
let range = Math.abs(yMax - yMin) | ||
const gl = w.globals | ||
const xaxisCnf = w.config.xaxis | ||
const yaxisCnf = w.config.yaxis[index] | ||
let gotMin = yaxisCnf.min !== undefined && yaxisCnf.min !== null | ||
let gotMax = yaxisCnf.max !== undefined && yaxisCnf.min !== null | ||
let gotStepSize = | ||
yaxisCnf.stepSize !== undefined && yaxisCnf.stepSize !== null | ||
let gotTickAmount = | ||
yaxisCnf.tickAmount !== undefined && yaxisCnf.tickAmount !== null | ||
// The most ticks we can fit into the svg chart dimensions | ||
const maxTicks = | ||
((gl.isBarHorizontal ? gl.svgWidth : gl.svgHeight) - 100) / 15 // Guestimate | ||
let ticks = gotTickAmount ? yaxisCnf.tickAmount : 10 | ||
ticks = this._adjustTicksForSmallRange(ticks, index, range) | ||
// In case we have a multi axis chart: | ||
// Ensure subsequent series start with the same tickAmount as series[0], | ||
// because the tick lines are drawn based on series[0]. This does not | ||
// override user defined options for any series. | ||
if (gl.isMultipleYAxis && !gotTickAmount && gl.multiAxisTickAmount > 0) { | ||
ticks = gl.multiAxisTickAmount | ||
gotTickAmount = true | ||
} | ||
if (ticks === 'dataPoints') { | ||
ticks = w.globals.dataPoints - 1 | ||
ticks = gl.dataPoints - 1 | ||
} else { | ||
// Ensure ticks is an integer | ||
ticks = Math.abs(Math.round(ticks)) | ||
} | ||
@@ -31,10 +53,3 @@ | ||
yMax = ticks | ||
let linearScale = this.linearScale( | ||
yMin, | ||
yMax, | ||
ticks, | ||
index, | ||
w.config.yaxis[index].stepSize | ||
) | ||
return linearScale | ||
gl.allSeriesCollapsed = false | ||
} | ||
@@ -45,4 +60,8 @@ | ||
// adjust the min/max again | ||
console.warn('axis.min cannot be greater than axis.max') | ||
yMax = yMin + 0.1 | ||
console.warn( | ||
'axis.min cannot be greater than axis.max: swapping min and max' | ||
) | ||
let temp = yMax | ||
yMax = yMin | ||
yMin = temp | ||
} else if (yMin === yMax) { | ||
@@ -52,11 +71,8 @@ // If yMin and yMax are identical, then | ||
// make a graph. Also avoids division by zero errors. | ||
yMin = yMin === 0 ? 0 : yMin - 0.5 // some small value | ||
yMax = yMax === 0 ? 2 : yMax + 0.5 // some small value | ||
yMin = yMin === 0 ? 0 : yMin - 1 // choose an integer in case yValueDecimals=0 | ||
yMax = yMax === 0 ? 2 : yMax + 1 // choose an integer in case yValueDecimals=0 | ||
} | ||
// Calculate Min amd Max graphical labels and graph | ||
// increments. The number of ticks defaults to | ||
// 10 which is the SUGGESTED value. Any tick value | ||
// entered is used as a suggested value which is | ||
// adjusted to be a 'pretty' value. | ||
// increments. | ||
// | ||
@@ -67,91 +83,286 @@ // Output will be an array of the Y axis values that | ||
if ( | ||
range < 1 && | ||
NO_MIN_MAX_PROVIDED && | ||
(w.config.chart.type === 'candlestick' || | ||
w.config.series[index].type === 'candlestick' || | ||
w.config.chart.type === 'boxPlot' || | ||
w.config.series[index].type === 'boxPlot' || | ||
w.globals.isRangeData) | ||
) { | ||
/* fix https://github.com/apexcharts/apexcharts.js/issues/430 */ | ||
yMax = yMax * 1.01 | ||
if (ticks < 1) { | ||
ticks = 1 | ||
} | ||
let tiks = ticks | ||
let tiks = ticks + 1 | ||
// Adjust ticks if needed | ||
if (tiks < 2) { | ||
tiks = 2 | ||
} else if (tiks > 2) { | ||
tiks -= 2 | ||
// Determine Range | ||
let range = Math.abs(yMax - yMin) | ||
if (yaxisCnf.forceNiceScale) { | ||
// Snap min or max to zero if close | ||
let proximityRatio = 0.15 | ||
if (!gotMin && yMin > 0 && yMin / range < proximityRatio) { | ||
yMin = 0 | ||
gotMin = true | ||
} | ||
if (!gotMax && yMax < 0 && -yMax / range < proximityRatio) { | ||
yMax = 0 | ||
gotMax = true | ||
} | ||
range = Math.abs(yMax - yMin) | ||
} | ||
// Get raw step value | ||
let tempStep = range / tiks | ||
// Calculate pretty step value | ||
// Calculate a pretty step value based on ticks | ||
let mag = Math.floor(Utils.log10(tempStep)) | ||
// Initial stepSize | ||
let stepSize = range / tiks | ||
let niceStep = stepSize | ||
let mag = Math.floor(Math.log10(niceStep)) | ||
let magPow = Math.pow(10, mag) | ||
let magMsd = Math.round(tempStep / magPow) | ||
if (magMsd < 1) { | ||
magMsd = 1 | ||
} | ||
let stepSize = magMsd * magPow | ||
// ceil() is used below in conjunction with the values populating | ||
// niceScaleAllowedMagMsd[][] to ensure that (niceStep * tiks) | ||
// produces a range that doesn't clip data points after stretching | ||
// the raw range out a little to match the prospective new range. | ||
let magMsd = Math.ceil(niceStep / magPow) | ||
// See globals.js for info on what niceScaleAllowedMagMsd does | ||
magMsd = gl.niceScaleAllowedMagMsd[gl.yValueDecimal === 0 ? 0 : 1][magMsd] | ||
niceStep = magMsd * magPow | ||
if (w.config.yaxis[index].stepSize) { | ||
stepSize = w.config.yaxis[index].stepSize | ||
} | ||
// Initial stepSize | ||
stepSize = niceStep | ||
// Get step value | ||
if ( | ||
w.globals.isBarHorizontal && | ||
w.config.xaxis.stepSize && | ||
w.config.xaxis.type !== 'datetime' | ||
gl.isBarHorizontal && | ||
xaxisCnf.stepSize && | ||
xaxisCnf.type !== 'datetime' | ||
) { | ||
stepSize = w.config.xaxis.stepSize | ||
stepSize = xaxisCnf.stepSize | ||
gotStepSize = true | ||
} else if (gotStepSize) { | ||
stepSize = yaxisCnf.stepSize | ||
} | ||
// build Y label array. | ||
// Lower and upper bounds calculations | ||
let lb = stepSize * Math.floor(yMin / stepSize) | ||
let ub = stepSize * Math.ceil(yMax / stepSize) | ||
// Build array | ||
let val = lb | ||
if (NO_MIN_MAX_PROVIDED && range > 2) { | ||
while (1) { | ||
result.push(Utils.stripNumber(val, 7)) | ||
val += stepSize | ||
if (val > ub) { | ||
break | ||
if (gotStepSize) { | ||
if (yaxisCnf.forceNiceScale) { | ||
// Check that given stepSize is sane with respect to the range. | ||
// | ||
// The user can, by setting forceNiceScale = true, | ||
// define a stepSize that will be scaled to useful value before | ||
// it's checked for consistency. | ||
// | ||
// If, for example, the range = 4 and the user defined stepSize = 8 | ||
// (or 8000 or 0.0008, etc), then stepSize is inapplicable as | ||
// it is. Reducing it to 0.8 will fit with 5 ticks. | ||
// | ||
if (Math.round(Math.log10(stepSize)) != mag) { | ||
let ref = range / ticks | ||
while (stepSize < ref) { | ||
stepSize *= 10 | ||
} | ||
while (stepSize > ref) { | ||
stepSize /= 10 | ||
} | ||
} | ||
} | ||
} | ||
return { | ||
result, | ||
niceMin: result[0], | ||
niceMax: result[result.length - 1], | ||
// Start applying some rules | ||
if (gotMin && gotMax) { | ||
let crudeStep = range / tiks | ||
// min and max (range) cannot be changed | ||
if (gotTickAmount) { | ||
if (gotStepSize) { | ||
if (Utils.mod(range, stepSize) != 0) { | ||
// stepSize conflicts with range | ||
let gcdStep = Utils.getGCD(stepSize, crudeStep) | ||
// gcdStep is a multiple of range because crudeStep is a multiple. | ||
// gcdStep is also a multiple of stepSize, so it partially honoured | ||
// All three could be equal, which would be very nice | ||
// if the computed stepSize generates too many ticks they will be | ||
// reduced later, unless the number is prime, in which case, | ||
// the chart will display all of them or just one (plus the X axis) | ||
// depending on svg dimensions. Setting forceNiceScale: true will force | ||
// the display of at least the default number of ticks. | ||
if (crudeStep / gcdStep < 10) { | ||
stepSize = gcdStep | ||
} else { | ||
// stepSize conflicts and no reasonable adjustment, but must | ||
// honour tickAmount | ||
stepSize = crudeStep | ||
} | ||
} else { | ||
// stepSize fits | ||
if (Utils.mod(stepSize, crudeStep) == 0) { | ||
// crudeStep is a multiple of stepSize, or vice versa | ||
// we know crudeStep will generate tickAmount ticks | ||
stepSize = crudeStep | ||
} else { | ||
// stepSize conflicts with tickAmount | ||
// if the user is setting up a multi-axis chart and wants | ||
// synced axis ticks then they should not define stepSize | ||
// or ensure there is no conflict between any of their options | ||
// on any axis. | ||
crudeStep = stepSize | ||
// De-prioritizing ticks from now on | ||
gotTickAmount = false | ||
} | ||
} | ||
} else { | ||
// no user stepSize, honour ticks | ||
stepSize = crudeStep | ||
} | ||
} else { | ||
// default ticks in use | ||
if (gotStepSize) { | ||
if (Utils.mod(range, stepSize) == 0) { | ||
// stepSize fits | ||
crudeStep = stepSize | ||
} else { | ||
stepSize = crudeStep | ||
} | ||
} else { | ||
// no user stepSize | ||
tiks = Math.round(range / niceStep) | ||
crudeStep = range / tiks | ||
if (Utils.mod(range, stepSize) != 0) { | ||
// stepSize doesn't fit | ||
let gcdStep = Utils.getGCD(range, niceStep) | ||
if (niceStep / gcdStep < 10) { | ||
crudeStep = gcdStep | ||
} | ||
stepSize = crudeStep | ||
} else { | ||
// stepSize fits | ||
crudeStep = stepSize | ||
} | ||
} | ||
} | ||
tiks = Math.round(range / stepSize) | ||
} else { | ||
result = [] | ||
let v = yMin | ||
result.push(Utils.stripNumber(v, 7)) | ||
let valuesDivider = Math.abs(yMax - yMin) / ticks | ||
for (let i = 0; i <= ticks; i++) { | ||
v = v + valuesDivider | ||
result.push(v) | ||
// Snap range to ticks | ||
if (!gotMin && !gotMax) { | ||
if (gotTickAmount) { | ||
// Allow a half-stepSize shift if series doesn't cross the X axis | ||
// to ensure graph doesn't clip. Not if it does cross, in order | ||
// to keep the 0 aligned with a grid line in multi axis charts. | ||
let shift = stepSize / (yMax - yMin > yMax ? 1 : 2) | ||
yMin = shift * Math.floor(yMin / shift) | ||
yMax = yMin + stepSize * tiks | ||
} else { | ||
yMin = stepSize * Math.floor(yMin / stepSize) | ||
yMax = stepSize * Math.ceil(yMax / stepSize) | ||
} | ||
} else if (gotMax) { | ||
if (gotTickAmount) { | ||
yMin = yMax - stepSize * tiks | ||
} else { | ||
yMin = stepSize * Math.floor(yMin / stepSize) | ||
} | ||
} else if (gotMin) { | ||
if (gotTickAmount) { | ||
yMax = yMin + stepSize * tiks | ||
} else { | ||
yMax = stepSize * Math.ceil(yMax / stepSize) | ||
} | ||
} | ||
range = Math.abs(yMax - yMin) | ||
// Final check and possible adjustment of stepSize to prevent | ||
// overridding the user's min or max choice. | ||
stepSize = Utils.getGCD(range, stepSize) | ||
tiks = Math.round(range / stepSize) | ||
} | ||
if (result[result.length - 2] >= yMax) { | ||
result.pop() | ||
// Shrinkwrap ticks to the range | ||
if (!gotTickAmount && !(gotMin || gotMax)) { | ||
tiks = Math.ceil((range - jsPrecision) / (stepSize + jsPrecision)) | ||
// No user tickAmount, or min or max, we are free to adjust to avoid a | ||
// prime number. This helps when reducing ticks for small svg dimensions. | ||
if (tiks > 16 && Utils.getPrimeFactors(tiks).length < 2) { | ||
tiks++ | ||
} | ||
} | ||
return { | ||
result, | ||
niceMin: result[0], | ||
niceMax: result[result.length - 1], | ||
// Record final tiks for use by other series that call niceScale(). | ||
// Note: some don't, like logarithmicScale(), etc. | ||
if (gl.isMultipleYAxis && gl.multiAxisTickAmount == 0) { | ||
gl.multiAxisTickAmount = tiks | ||
} | ||
if ( | ||
tiks > maxTicks && | ||
(!(gotTickAmount || gotStepSize) || yaxisCnf.forceNiceScale) | ||
) { | ||
// Reduce the number of ticks nicely if chart svg dimensions shrink too far. | ||
// The reduced tick set should always be a subset of the full set. | ||
// | ||
// This following products of prime factors method works as follows: | ||
// We compute the prime factors of the full tick count (tiks), then all the | ||
// possible products of those factors in order from smallest to biggest, | ||
// until we find a product P such that: tiks/P < maxTicks. | ||
// | ||
// Example: | ||
// Computing products of the prime factors of 30. | ||
// | ||
// tiks | pf | 1 2 3 4 5 6 <-- compute order | ||
// -------------------------------------------------- | ||
// 30 | 5 | 5 5 5 <-- Multiply all | ||
// | 3 | 3 3 3 3 <-- primes in each | ||
// | 2 | 2 2 2 <-- column = P | ||
// -------------------------------------------------- | ||
// 15 10 6 5 2 1 <-- tiks/P | ||
// | ||
// tiks = 30 has prime factors [2, 3, 5] | ||
// The loop below computes the products [2,3,5,6,15,30]. | ||
// The last product of P = 2*3*5 is skipped since 30/P = 1. | ||
// This yields tiks/P = [15,10,6,5,2,1], checked in order until | ||
// tiks/P < maxTicks. | ||
// | ||
// Pros: | ||
// 1) The ticks in the reduced set are always members of the | ||
// full set of ticks. | ||
// Cons: | ||
// 1) None: if tiks is prime, we get all or one, nothing between, so | ||
// the worst case is to display all, which is the status quo. Really | ||
// only a problem visually for larger tick numbers, say, > 7. | ||
// | ||
let pf = Utils.getPrimeFactors(tiks) | ||
let last = pf.length - 1 | ||
let tt = tiks | ||
reduceLoop: for (var xFactors = 0; xFactors < last; xFactors++) { | ||
for (var lowest = 0; lowest <= last - xFactors; lowest++) { | ||
let stop = Math.min(lowest + xFactors, last) | ||
let t = tt | ||
let div = 1 | ||
for (var next = lowest; next <= stop; next++) { | ||
div *= pf[next] | ||
} | ||
t /= div | ||
if (t < maxTicks) { | ||
tt = t | ||
break reduceLoop | ||
} | ||
} | ||
} | ||
// Only reduce tiks all the way down to 1 (increase stepSize to range) | ||
// if forceNiceScale = true, to give the user the option if tiks is | ||
// prime and > maxTicks, which may result in premature removal of all but | ||
// the last tick. It will not be immediately obvious why that has occured. | ||
if (tt === tiks && yaxisCnf.forceNiceScale) { | ||
stepSize = range | ||
} else { | ||
stepSize = range / tt | ||
} | ||
} | ||
// build Y label array. | ||
let val = yMin - stepSize | ||
// Ensure we don't under/over shoot due to JS precision errors. | ||
// This also fixes (amongst others): | ||
// https://github.com/apexcharts/apexcharts.js/issues/430 | ||
let err = stepSize * jsPrecision | ||
do { | ||
val += stepSize | ||
result.push(Utils.stripNumber(val, 7)) | ||
} while (yMax - val > err) | ||
return { | ||
result, | ||
niceMin: result[0], | ||
niceMax: result[result.length - 1], | ||
} | ||
} | ||
linearScale(yMin, yMax, ticks = 5, index = 0, step = undefined) { | ||
linearScale(yMin, yMax, ticks = 10, index = 0, step = undefined) { | ||
let range = Math.abs(yMax - yMin) | ||
@@ -288,3 +499,2 @@ | ||
gl.allSeriesCollapsed = false | ||
gl.yAxisScale[index] = this.logarithmicScale(minY, maxY, y.logBase) | ||
gl.yAxisScale[index] = y.forceNiceScale | ||
@@ -298,4 +508,4 @@ ? this.logarithmicScaleNice(minY, maxY, y.logBase) | ||
0, | ||
5, | ||
5, | ||
10, | ||
10, | ||
index, | ||
@@ -307,26 +517,3 @@ cnf.yaxis[index].stepSize | ||
gl.allSeriesCollapsed = false | ||
if ((y.min !== undefined || y.max !== undefined) && !y.forceNiceScale) { | ||
// fix https://github.com/apexcharts/apexcharts.js/issues/492 | ||
gl.yAxisScale[index] = this.linearScale( | ||
minY, | ||
maxY, | ||
y.tickAmount, | ||
index, | ||
cnf.yaxis[index].stepSize | ||
) | ||
} else { | ||
const noMinMaxProvided = | ||
(cnf.yaxis[index].max === undefined && | ||
cnf.yaxis[index].min === undefined) || | ||
cnf.yaxis[index].forceNiceScale | ||
gl.yAxisScale[index] = this.niceScale( | ||
minY, | ||
maxY, | ||
y.tickAmount ? y.tickAmount : diff < 5 && diff > 1 ? diff + 1 : 5, | ||
index, | ||
// fix https://github.com/apexcharts/apexcharts.js/issues/397 | ||
noMinMaxProvided | ||
) | ||
} | ||
gl.yAxisScale[index] = this.niceScale(minY, maxY, index) | ||
} | ||
@@ -342,3 +529,3 @@ } | ||
// no data in the chart. Either all series collapsed or user passed a blank array | ||
gl.xAxisScale = this.linearScale(0, 5, 5) | ||
gl.xAxisScale = this.linearScale(0, 10, 10) | ||
} else { | ||
@@ -350,5 +537,5 @@ gl.xAxisScale = this.linearScale( | ||
? w.config.xaxis.tickAmount | ||
: diff < 5 && diff > 1 | ||
: diff < 10 && diff > 1 | ||
? diff + 1 | ||
: 5, | ||
: 10, | ||
0, | ||
@@ -355,0 +542,0 @@ w.config.xaxis.stepSize |
@@ -961,6 +961,2 @@ import Utils from '../../utils/Utils' | ||
polarArea() { | ||
this.opts.yaxis[0].tickAmount = this.opts.yaxis[0].tickAmount | ||
? this.opts.yaxis[0].tickAmount | ||
: 6 | ||
return { | ||
@@ -967,0 +963,0 @@ chart: { |
@@ -72,2 +72,3 @@ import Utils from './../../utils/Utils' | ||
gl.xTickAmount = 0 | ||
gl.multiAxisTickAmount = 0 | ||
} | ||
@@ -213,3 +214,22 @@ | ||
translateXAxisX: 0, | ||
tooltip: null | ||
tooltip: null, | ||
// Rules for niceScaleAllowedMagMsd: | ||
// 1) An array of two arrays only ([[],[]]): | ||
// * array[0][]: influences labelling of data series that contain only integers | ||
// - must contain only integers (or expect ugly ticks) | ||
// * array[1][]: influences labelling of data series that contain at least one float | ||
// - may contain floats | ||
// * both arrays: | ||
// - each array[][i] ideally satisfy: 10 mod array[][i] == 0 (or expect ugly ticks) | ||
// - to avoid clipping data point keep each array[][i] >= i | ||
// 2) each array[i][] contains 11 values, for all possible index values 0..10. | ||
// array[][0] should not be needed (not proven) but ensures non-zero is returned. | ||
// | ||
// Users can effectively force their preferred "magMsd" through stepSize and | ||
// forceNiceScale. With forceNiceScale: true, stepSize becomes normalizable to the | ||
// axis's min..max range, which allows users to set stepSize to an integer 1..10, for | ||
// example, stepSize: 3. This value will be preferred to the value determined through | ||
// this array. The range-normalized value is checked for consistency with other | ||
// user defined options and will be ignored if inconsistent. | ||
niceScaleAllowedMagMsd: [[1,1,2,5,5,5,10,10,10,10,10],[1,1,2,5,5,5,10,10,10,10,10]] | ||
} | ||
@@ -216,0 +236,0 @@ } |
@@ -404,4 +404,42 @@ /* | ||
} | ||
// | ||
// Find the Greatest Common Divisor of two numbers | ||
// | ||
static getGCD(a, b, p = 7) { | ||
let big = Math.pow(10, (p - Math.floor(Math.log10(Math.max(a, b))))) | ||
a = Math.round(Math.abs(a) * big) | ||
b = Math.round(Math.abs(b) * big) | ||
while (b) { | ||
let t = b | ||
b = a % b | ||
a = t | ||
} | ||
return a / big | ||
} | ||
static getPrimeFactors(n) { | ||
const factors = [] | ||
let divisor = 2 | ||
while (n >= 2) { | ||
if (n % divisor == 0) { | ||
factors.push(divisor) | ||
n = n / divisor | ||
} else { | ||
divisor++ | ||
} | ||
} | ||
return factors | ||
} | ||
static mod(a, b, p = 7) { | ||
let big = Math.pow(10, (p - Math.floor(Math.log10(Math.max(a, b))))) | ||
a = Math.round(Math.abs(a) * big) | ||
b = Math.round(Math.abs(b) * big) | ||
return (a % b) / big | ||
} | ||
} | ||
export default Utils |
@@ -850,4 +850,6 @@ // Typescript declarations for Apex class and module. | ||
type ApexMarkerShape = "circle" | "square" | "rect" | string[] | ||
type MarkerShapeOptions = "circle" | "square" | "rect" | 'x' | 'X' | 'plus' | '+' | ||
type ApexMarkerShape = MarkerShapeOptions | MarkerShapeOptions[] | ||
type ApexDiscretePoint = { | ||
@@ -854,0 +856,0 @@ seriesIndex?: number |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
4486341
192
72627