import Defaults from './Defaults' import Utils from './../../utils/Utils' import Options from './Options' /** * ApexCharts Config Class for extending user options with pre-defined ApexCharts config. * * @module Config **/ export default class Config { constructor(opts) { this.opts = opts } init({ responsiveOverride }) { let opts = this.opts let options = new Options() let defaults = new Defaults(opts) this.chartType = opts.chart.type if (this.chartType === 'histogram') { // technically, a histogram can be drawn by a column chart with no spaces in between opts.chart.type = 'bar' opts = Utils.extend( { plotOptions: { bar: { columnWidth: '99.99%' } } }, opts ) } opts = this.extendYAxis(opts) opts = this.extendAnnotations(opts) let config = options.init() let newDefaults = {} if (opts && typeof opts === 'object') { let chartDefaults = {} const chartTypes = [ 'line', 'area', 'bar', 'candlestick', 'boxPlot', 'rangeBar', 'histogram', 'bubble', 'scatter', 'heatmap', 'treemap', 'pie', 'polarArea', 'donut', 'radar', 'radialBar' ] if (chartTypes.indexOf(opts.chart.type) !== -1) { chartDefaults = defaults[opts.chart.type]() } else { chartDefaults = defaults.line() } if (opts.chart.brush && opts.chart.brush.enabled) { chartDefaults = defaults.brush(chartDefaults) } if (opts.chart.stacked && opts.chart.stackType === '100%') { opts = defaults.stacked100(opts) } // If user has specified a dark theme, make the tooltip dark too this.checkForDarkTheme(window.Apex) // check global window Apex options this.checkForDarkTheme(opts) // check locally passed options opts.xaxis = opts.xaxis || window.Apex.xaxis || {} // an important boolean needs to be set here // otherwise all the charts will have this flag set to true window.Apex.xaxis is set globally if (!responsiveOverride) { opts.xaxis.convertedCatToNumeric = false } opts = this.checkForCatToNumericXAxis(this.chartType, chartDefaults, opts) if ( (opts.chart.sparkline && opts.chart.sparkline.enabled) || (window.Apex.chart && window.Apex.chart.sparkline && window.Apex.chart.sparkline.enabled) ) { chartDefaults = defaults.sparkline(chartDefaults) } newDefaults = Utils.extend(config, chartDefaults) } // config should cascade in this fashion // default-config < global-apex-variable-config < user-defined-config // get GLOBALLY defined options and merge with the default config let mergedWithDefaultConfig = Utils.extend(newDefaults, window.Apex) // get the merged config and extend with user defined config config = Utils.extend(mergedWithDefaultConfig, opts) // some features are not supported. those mismatches should be handled config = this.handleUserInputErrors(config) return config } checkForCatToNumericXAxis(chartType, chartDefaults, opts) { let defaults = new Defaults(opts) const isBarHorizontal = (chartType === 'bar' || chartType === 'boxPlot') && opts.plotOptions && opts.plotOptions.bar && opts.plotOptions.bar.horizontal const unsupportedZoom = chartType === 'pie' || chartType === 'polarArea' || chartType === 'donut' || chartType === 'radar' || chartType === 'radialBar' || chartType === 'heatmap' const notNumericXAxis = opts.xaxis.type !== 'datetime' && opts.xaxis.type !== 'numeric' let tickPlacement = opts.xaxis.tickPlacement ? opts.xaxis.tickPlacement : chartDefaults.xaxis && chartDefaults.xaxis.tickPlacement if ( !isBarHorizontal && !unsupportedZoom && notNumericXAxis && tickPlacement !== 'between' ) { opts = defaults.convertCatToNumeric(opts) } return opts } extendYAxis(opts, w) { let options = new Options() if ( typeof opts.yaxis === 'undefined' || !opts.yaxis || (Array.isArray(opts.yaxis) && opts.yaxis.length === 0) ) { opts.yaxis = {} } // extend global yaxis config (only if object is provided / not an array) if ( opts.yaxis.constructor !== Array && window.Apex.yaxis && window.Apex.yaxis.constructor !== Array ) { opts.yaxis = Utils.extend(opts.yaxis, window.Apex.yaxis) } // as we can't extend nested object's array with extend, we need to do it first // user can provide either an array or object in yaxis config if (opts.yaxis.constructor !== Array) { // convert the yaxis to array if user supplied object opts.yaxis = [Utils.extend(options.yAxis, opts.yaxis)] } else { opts.yaxis = Utils.extendArray(opts.yaxis, options.yAxis) } let isLogY = false opts.yaxis.forEach((y) => { if (y.logarithmic) { isLogY = true } }) let series = opts.series if (w && !series) { series = w.config.series } // A logarithmic chart works correctly when each series has a corresponding y-axis // If this is not the case, we manually create yaxis for multi-series log chart if (isLogY && series.length !== opts.yaxis.length && series.length) { opts.yaxis = series.map((s, i) => { if (!s.name) { series[i].name = `series-${i + 1}` } if (opts.yaxis[i]) { opts.yaxis[i].seriesName = series[i].name return opts.yaxis[i] } else { const newYaxis = Utils.extend(options.yAxis, opts.yaxis[0]) newYaxis.show = false return newYaxis } }) } if (isLogY && series.length > 1 && series.length !== opts.yaxis.length) { console.warn( 'A multi-series logarithmic chart should have equal number of series and y-axes. Please make sure to equalize both.' ) } return opts } // annotations also accepts array, so we need to extend them manually extendAnnotations(opts) { if (typeof opts.annotations === 'undefined') { opts.annotations = {} opts.annotations.yaxis = [] opts.annotations.xaxis = [] opts.annotations.points = [] } opts = this.extendYAxisAnnotations(opts) opts = this.extendXAxisAnnotations(opts) opts = this.extendPointAnnotations(opts) return opts } extendYAxisAnnotations(opts) { let options = new Options() opts.annotations.yaxis = Utils.extendArray( typeof opts.annotations.yaxis !== 'undefined' ? opts.annotations.yaxis : [], options.yAxisAnnotation ) return opts } extendXAxisAnnotations(opts) { let options = new Options() opts.annotations.xaxis = Utils.extendArray( typeof opts.annotations.xaxis !== 'undefined' ? opts.annotations.xaxis : [], options.xAxisAnnotation ) return opts } extendPointAnnotations(opts) { let options = new Options() opts.annotations.points = Utils.extendArray( typeof opts.annotations.points !== 'undefined' ? opts.annotations.points : [], options.pointAnnotation ) return opts } checkForDarkTheme(opts) { if (opts.theme && opts.theme.mode === 'dark') { if (!opts.tooltip) { opts.tooltip = {} } if (opts.tooltip.theme !== 'light') { opts.tooltip.theme = 'dark' } if (!opts.chart.foreColor) { opts.chart.foreColor = '#f6f7f8' } if (!opts.chart.background) { opts.chart.background = '#424242' } if (!opts.theme.palette) { opts.theme.palette = 'palette4' } } } handleUserInputErrors(opts) { let config = opts // conflicting tooltip option. intersect makes sure to focus on 1 point at a time. Shared cannot be used along with it if (config.tooltip.shared && config.tooltip.intersect) { throw new Error( 'tooltip.shared cannot be enabled when tooltip.intersect is true. Turn off any other option by setting it to false.' ) } if (config.chart.type === 'bar' && config.plotOptions.bar.horizontal) { // No multiple yaxis for bars if (config.yaxis.length > 1) { throw new Error( 'Multiple Y Axis for bars are not supported. Switch to column chart by setting plotOptions.bar.horizontal=false' ) } // if yaxis is reversed in horizontal bar chart, you should draw the y-axis on right side 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 } if (config.chart.type === 'bar' || config.chart.type === 'rangeBar') { if (config.tooltip.shared) { if ( config.xaxis.crosshairs.width === 'barWidth' && config.series.length > 1 ) { config.xaxis.crosshairs.width = 'tickWidth' } } } if ( config.chart.type === 'candlestick' || config.chart.type === 'boxPlot' ) { if (config.yaxis[0].reversed) { console.warn( `Reversed y-axis in ${config.chart.type} chart is not supported.` ) config.yaxis[0].reversed = false } } return config } }