apexcharts
Version:
A JavaScript Chart Library
1,297 lines (1,239 loc) • 26.2 kB
JavaScript
// @ts-check
import Utils from '../../utils/Utils'
import DateTime from '../../utils/DateTime'
import Formatters from '../Formatters'
/**
* ApexCharts Default Class for setting default options for all chart types.
*
* @module Defaults
**/
/** @param {{isTimeline: any, seriesIndex: any, dataPointIndex: any, y1: any, y2: any, w: any}} opts */
const getRangeValues = ({
isTimeline,
seriesIndex,
dataPointIndex,
y1,
y2,
w,
}) => {
let start = w.rangeData.seriesRangeStart[seriesIndex][dataPointIndex]
let end = w.rangeData.seriesRangeEnd[seriesIndex][dataPointIndex]
let ylabel = w.labelData.labels[dataPointIndex]
let seriesName = w.config.series[seriesIndex].name
? w.config.series[seriesIndex].name
: ''
const yLbFormatter = w.formatters.ttKeyFormatter
const yLbTitleFormatter = w.config.tooltip.y.title.formatter
const opts = {
w,
seriesIndex,
dataPointIndex,
start,
end,
}
if (typeof yLbTitleFormatter === 'function') {
seriesName = yLbTitleFormatter(seriesName, opts)
}
if (w.config.series[seriesIndex].data[dataPointIndex]?.x) {
ylabel = w.config.series[seriesIndex].data[dataPointIndex].x
}
if (!isTimeline) {
if (w.config.xaxis.type === 'datetime') {
const xFormat = new Formatters(w)
ylabel = xFormat.xLabelFormat(
w.formatters.ttKeyFormatter,
ylabel,
ylabel,
{
i: undefined,
dateFormatter: new DateTime(w).formatDate,
w,
},
)
}
}
if (typeof yLbFormatter === 'function') {
ylabel = yLbFormatter(ylabel, opts)
}
if (Number.isFinite(y1) && Number.isFinite(y2)) {
start = y1
end = y2
}
let startVal = ''
let endVal = ''
const color = w.globals.colors[seriesIndex]
if (w.config.tooltip.x.formatter === undefined) {
if (w.config.xaxis.type === 'datetime') {
const datetimeObj = new DateTime(w)
startVal = datetimeObj.formatDate(
datetimeObj.getDate(start),
w.config.tooltip.x.format,
)
endVal = datetimeObj.formatDate(
datetimeObj.getDate(end),
w.config.tooltip.x.format,
)
} else {
startVal = start
endVal = end
}
} else {
startVal = w.config.tooltip.x.formatter(start)
endVal = w.config.tooltip.x.formatter(end)
}
return { start, end, startVal, endVal, ylabel, color, seriesName }
}
/**
* @param {Record<string, any>} opts
*/
const buildRangeTooltipHTML = (opts) => {
let { color, seriesName, ylabel, start, end, seriesIndex, dataPointIndex } =
opts
const formatter =
opts.w.globals.tooltip.tooltipLabels.getFormatters(seriesIndex)
start = formatter.yLbFormatter(start)
end = formatter.yLbFormatter(end)
const val = formatter.yLbFormatter(
opts.w.seriesData.series[seriesIndex][dataPointIndex],
)
let valueHTML = ''
const rangeValues = `<span class="value start-value">
${start}
</span> <span class="separator">-</span> <span class="value end-value">
${end}
</span>`
if (opts.w.globals.comboCharts) {
if (
opts.w.config.series[seriesIndex].type === 'rangeArea' ||
opts.w.config.series[seriesIndex].type === 'rangeBar'
) {
valueHTML = rangeValues
} else {
valueHTML = `<span>${val}</span>`
}
} else {
valueHTML = rangeValues
}
return (
'<div class="apexcharts-tooltip-rangebar">' +
'<div> <span class="series-name" style="color: ' +
color +
'">' +
(seriesName ? seriesName : '') +
'</span></div>' +
'<div> <span class="category">' +
ylabel +
': </span> ' +
valueHTML +
' </div>' +
'</div>'
)
}
export default class Defaults {
/**
* @param {Record<string, any>} opts
*/
constructor(opts) {
this.opts = opts
}
hideYAxis() {
this.opts.yaxis[0].show = false
this.opts.yaxis[0].title.text = ''
this.opts.yaxis[0].axisBorder.show = false
this.opts.yaxis[0].axisTicks.show = false
this.opts.yaxis[0].floating = true
}
line() {
return {
dataLabels: {
enabled: false,
},
stroke: {
width: 5,
curve: 'straight',
},
markers: {
size: 0,
hover: {
sizeOffset: 6,
},
},
xaxis: {
crosshairs: {
width: 1,
},
},
}
}
/**
* @param {Record<string, any>} defaults
*/
sparkline(defaults) {
this.hideYAxis()
const ret = {
grid: {
show: false,
padding: {
left: 0,
right: 0,
top: 0,
bottom: 0,
},
},
legend: {
show: false,
},
xaxis: {
labels: {
show: false,
},
tooltip: {
enabled: false,
},
axisBorder: {
show: false,
},
axisTicks: {
show: false,
},
},
chart: {
toolbar: {
show: false,
},
zoom: {
enabled: false,
},
},
dataLabels: {
enabled: false,
},
}
return Utils.extend(defaults, ret)
}
slope() {
this.hideYAxis()
return {
chart: {
toolbar: {
show: false,
},
zoom: {
enabled: false,
},
},
dataLabels: {
enabled: true,
/**
* @param {any} val
* @param {Record<string, any>} opts
*/
formatter(val, opts) {
const seriesName = opts.w.config.series[opts.seriesIndex].name
return val !== null ? seriesName + ': ' + val : ''
},
background: {
enabled: false,
},
offsetX: -5,
},
grid: {
xaxis: {
lines: {
show: true,
},
},
yaxis: {
lines: {
show: false,
},
},
},
xaxis: {
position: 'top',
labels: {
style: {
fontSize: 14,
fontWeight: 900,
},
},
tooltip: {
enabled: false,
},
crosshairs: {
show: false,
},
},
markers: {
size: 8,
hover: {
sizeOffset: 1,
},
},
legend: {
show: false,
},
tooltip: {
shared: false,
intersect: true,
followCursor: true,
},
stroke: {
width: 5,
curve: 'straight',
},
}
}
bar() {
return {
chart: {
stacked: false,
},
plotOptions: {
bar: {
dataLabels: {
position: 'center',
},
},
},
dataLabels: {
style: {
colors: ['#fff'],
},
background: {
enabled: false,
},
},
stroke: {
width: 0,
lineCap: 'square',
},
fill: {
opacity: 0.85,
},
legend: {
markers: {
shape: 'square',
},
},
tooltip: {
shared: false,
intersect: true,
},
xaxis: {
tooltip: {
enabled: false,
},
tickPlacement: 'between',
crosshairs: {
width: 'barWidth',
position: 'back',
fill: {
type: 'gradient',
},
dropShadow: {
enabled: false,
},
stroke: {
width: 0,
},
},
},
}
}
funnel() {
this.hideYAxis()
return {
...this.bar(),
chart: {
animations: {
speed: 800,
animateGradually: {
enabled: false,
},
},
},
plotOptions: {
bar: {
horizontal: true,
borderRadiusApplication: 'around',
borderRadius: 0,
dataLabels: {
position: 'center',
},
},
},
grid: {
show: false,
padding: {
left: 0,
right: 0,
},
},
xaxis: {
labels: {
show: false,
},
tooltip: {
enabled: false,
},
axisBorder: {
show: false,
},
axisTicks: {
show: false,
},
},
}
}
candlestick() {
return {
stroke: {
width: 1,
},
fill: {
opacity: 1,
},
dataLabels: {
enabled: false,
},
tooltip: {
shared: true,
custom: (/** @type {any} */ { seriesIndex, dataPointIndex, w }) => {
return this._getBoxTooltip(
w,
seriesIndex,
dataPointIndex,
['Open', 'High', '', 'Low', 'Close'],
'candlestick',
)
},
},
states: {
active: {
filter: {
type: 'none',
},
},
},
xaxis: {
crosshairs: {
width: 1,
},
},
}
}
boxPlot() {
return {
chart: {
animations: {
dynamicAnimation: {
enabled: false,
},
},
},
stroke: {
width: 1,
colors: ['#24292e'],
},
dataLabels: {
enabled: false,
},
tooltip: {
shared: true,
custom: (/** @type {any} */ { seriesIndex, dataPointIndex, w }) => {
return this._getBoxTooltip(
w,
seriesIndex,
dataPointIndex,
['Minimum', 'Q1', 'Median', 'Q3', 'Maximum'],
'boxPlot',
)
},
},
markers: {
size: 7,
strokeWidth: 1,
strokeColors: '#111',
},
xaxis: {
crosshairs: {
width: 1,
},
},
}
}
rangeBar() {
/**
* @param {any} opts
*/
const handleTimelineTooltip = (opts) => {
const { color, seriesName, ylabel, startVal, endVal } = getRangeValues({
...opts,
isTimeline: true,
})
return buildRangeTooltipHTML({
...opts,
color,
seriesName,
ylabel,
start: startVal,
end: endVal,
})
}
/**
* @param {any} opts
*/
const handleRangeColumnTooltip = (opts) => {
const { color, seriesName, ylabel, start, end } = getRangeValues(opts)
return buildRangeTooltipHTML({
...opts,
color,
seriesName,
ylabel,
start,
end,
})
}
return {
chart: {
animations: {
animateGradually: false,
},
},
stroke: {
width: 0,
lineCap: 'square',
},
plotOptions: {
bar: {
borderRadius: 0,
dataLabels: {
position: 'center',
},
},
},
dataLabels: {
enabled: false,
/**
* @param {any} val
*/
formatter(
/** @type {any} */ val,
/** @type {any} */ { seriesIndex, dataPointIndex, w },
) {
const getVal = () => {
const start =
w.rangeData.seriesRangeStart[seriesIndex][dataPointIndex]
const end = w.rangeData.seriesRangeEnd[seriesIndex][dataPointIndex]
return end - start
}
if (w.globals.comboCharts) {
if (
w.config.series[seriesIndex].type === 'rangeBar' ||
w.config.series[seriesIndex].type === 'rangeArea'
) {
return getVal()
} else {
return val
}
} else {
return getVal()
}
},
background: {
enabled: false,
},
style: {
colors: ['#fff'],
},
},
markers: {
size: 10,
},
tooltip: {
shared: false,
followCursor: true,
/**
* @param {Record<string, any>} opts
*/
custom(opts) {
if (
opts.w.config.plotOptions &&
opts.w.config.plotOptions.bar &&
opts.w.config.plotOptions.bar.horizontal
) {
return handleTimelineTooltip(opts)
} else {
return handleRangeColumnTooltip(opts)
}
},
},
xaxis: {
tickPlacement: 'between',
tooltip: {
enabled: false,
},
crosshairs: {
stroke: {
width: 0,
},
},
},
}
}
/**
* @param {Record<string, any>} opts
*/
dumbbell(opts) {
if (!opts.plotOptions.bar?.barHeight) {
opts.plotOptions.bar.barHeight = 2
}
if (!opts.plotOptions.bar?.columnWidth) {
opts.plotOptions.bar.columnWidth = 2
}
return opts
}
area() {
return {
stroke: {
width: 4,
fill: {
type: 'solid',
gradient: {
inverseColors: false,
shade: 'light',
type: 'vertical',
opacityFrom: 0.65,
opacityTo: 0.5,
stops: [0, 100, 100],
},
},
},
fill: {
type: 'gradient',
gradient: {
inverseColors: false,
shade: 'light',
type: 'vertical',
opacityFrom: 0.65,
opacityTo: 0.5,
stops: [0, 100, 100],
},
},
markers: {
size: 0,
hover: {
sizeOffset: 6,
},
},
tooltip: {
followCursor: false,
},
}
}
rangeArea() {
/**
* @param {any} opts
*/
const handleRangeAreaTooltip = (opts) => {
const { color, seriesName, ylabel, start, end } = getRangeValues(opts)
return buildRangeTooltipHTML({
...opts,
color,
seriesName,
ylabel,
start,
end,
})
}
return {
stroke: {
curve: 'straight',
width: 0,
},
fill: {
type: 'solid',
opacity: 0.6,
},
markers: {
size: 0,
},
states: {
hover: {
filter: {
type: 'none',
},
},
active: {
filter: {
type: 'none',
},
},
},
tooltip: {
intersect: false,
shared: true,
followCursor: true,
/**
* @param {Record<string, any>} opts
*/
custom(opts) {
return handleRangeAreaTooltip(opts)
},
},
}
}
/**
* @param {Record<string, any>} defaults
*/
brush(defaults) {
const ret = {
chart: {
toolbar: {
autoSelected: 'selection',
show: false,
},
zoom: {
enabled: false,
},
},
dataLabels: {
enabled: false,
},
stroke: {
width: 1,
},
tooltip: {
enabled: false,
},
xaxis: {
tooltip: {
enabled: false,
},
},
}
return Utils.extend(defaults, ret)
}
/**
* @param {Record<string, any>} opts
*/
stacked100(opts) {
opts.dataLabels = opts.dataLabels || {}
opts.dataLabels.formatter = opts.dataLabels.formatter || undefined
const existingDataLabelFormatter = opts.dataLabels.formatter
/**
* @param {ApexYAxis} yaxe
* @param {number} index
*/
opts.yaxis.forEach((/** @type {any} */ yaxe, /** @type {any} */ index) => {
opts.yaxis[index].min = 0
opts.yaxis[index].max = 100
})
const isBar = opts.chart.type === 'bar'
if (isBar) {
opts.dataLabels.formatter =
existingDataLabelFormatter ||
/**
* @param {any} val
*/
function (val) {
if (typeof val === 'number') {
return val ? val.toFixed(0) + '%' : val
}
return val
}
}
return opts
}
stackedBars() {
const barDefaults = this.bar()
return {
...barDefaults,
plotOptions: {
...barDefaults.plotOptions,
bar: {
...barDefaults.plotOptions.bar,
borderRadiusApplication: 'end',
borderRadiusWhenStacked: 'last',
},
},
}
}
// This function removes the left and right spacing in chart for line/area/scatter if xaxis type = category for those charts by converting xaxis = numeric. Numeric/Datetime xaxis prevents the unnecessary spacing in the left/right of the chart area
/**
* @param {Record<string, any>} opts
*/
convertCatToNumeric(opts) {
opts.xaxis.convertedCatToNumeric = true
return opts
}
/**
* @param {Record<string, any>} opts
* @param {any} cats
*/
convertCatToNumericXaxis(opts, cats) {
opts.xaxis.type = 'numeric'
opts.xaxis.labels = opts.xaxis.labels || {}
opts.xaxis.labels.formatter =
opts.xaxis.labels.formatter ||
/**
* @param {any} val
*/
function (val) {
return Utils.isNumber(val) ? Math.floor(val) : val
}
const defaultFormatter = opts.xaxis.labels.formatter
let labels =
opts.xaxis.categories && opts.xaxis.categories.length
? opts.xaxis.categories
: opts.labels
if (cats && cats.length) {
/**
* @param {any} c
*/
labels = cats.map((/** @type {any} */ c) => {
return Array.isArray(c) ? c : String(c)
})
}
if (labels && labels.length) {
/**
* @param {any} val
*/
opts.xaxis.labels.formatter = function (val) {
return Utils.isNumber(val)
? defaultFormatter(labels[Math.floor(val) - 1])
: defaultFormatter(val)
}
}
opts.xaxis.categories = []
opts.labels = []
opts.xaxis.tickAmount = opts.xaxis.tickAmount || 'dataPoints'
return opts
}
bubble() {
return {
dataLabels: {
style: {
colors: ['#fff'],
},
},
tooltip: {
shared: false,
intersect: true,
},
xaxis: {
crosshairs: {
width: 0,
},
},
fill: {
type: 'solid',
gradient: {
shade: 'light',
inverse: true,
shadeIntensity: 0.55,
opacityFrom: 0.4,
opacityTo: 0.8,
},
},
}
}
scatter() {
return {
dataLabels: {
enabled: false,
},
tooltip: {
shared: false,
intersect: true,
},
markers: {
size: 6,
strokeWidth: 1,
hover: {
sizeOffset: 2,
},
},
}
}
heatmap() {
return {
chart: {
stacked: false,
},
fill: {
opacity: 1,
},
dataLabels: {
style: {
colors: ['#fff'],
},
},
stroke: {
colors: ['#fff'],
},
tooltip: {
followCursor: true,
marker: {
show: false,
},
x: {
show: false,
},
},
legend: {
position: 'top',
markers: {
shape: 'square',
},
},
grid: {
padding: {
right: 20,
},
},
}
}
treemap() {
return {
chart: {
zoom: {
enabled: false,
},
},
dataLabels: {
style: {
fontSize: 14,
fontWeight: 600,
colors: ['#fff'],
},
},
stroke: {
show: true,
width: 2,
colors: ['#fff'],
},
legend: {
show: false,
},
fill: {
opacity: 1,
gradient: {
stops: [0, 100],
},
},
tooltip: {
followCursor: true,
x: {
show: false,
},
},
grid: {
padding: {
left: 0,
right: 0,
},
},
xaxis: {
crosshairs: {
show: false,
},
tooltip: {
enabled: false,
},
},
}
}
pie() {
return {
chart: {
toolbar: {
show: false,
},
},
plotOptions: {
pie: {
donut: {
labels: {
show: false,
},
},
},
},
dataLabels: {
/**
* @param {number} val
*/
formatter(val) {
return val.toFixed(1) + '%'
},
style: {
colors: ['#fff'],
},
background: {
enabled: false,
},
dropShadow: {
enabled: true,
},
},
stroke: {
colors: ['#fff'],
},
fill: {
opacity: 1,
gradient: {
shade: 'light',
stops: [0, 100],
},
},
tooltip: {
theme: 'dark',
fillSeriesColor: true,
},
legend: {
position: 'right',
},
grid: {
padding: {
left: 0,
right: 0,
top: 0,
bottom: 0,
},
},
}
}
donut() {
return {
chart: {
toolbar: {
show: false,
},
},
dataLabels: {
/**
* @param {number} val
*/
formatter(val) {
return val.toFixed(1) + '%'
},
style: {
colors: ['#fff'],
},
background: {
enabled: false,
},
dropShadow: {
enabled: true,
},
},
stroke: {
colors: ['#fff'],
},
fill: {
opacity: 1,
gradient: {
shade: 'light',
shadeIntensity: 0.35,
stops: [80, 100],
opacityFrom: 1,
opacityTo: 1,
},
},
tooltip: {
theme: 'dark',
fillSeriesColor: true,
},
legend: {
position: 'right',
},
grid: {
padding: {
left: 0,
right: 0,
top: 0,
bottom: 0,
},
},
}
}
polarArea() {
return {
chart: {
toolbar: {
show: false,
},
},
dataLabels: {
/**
* @param {number} val
*/
formatter(val) {
return val.toFixed(1) + '%'
},
enabled: false,
},
stroke: {
show: true,
width: 2,
},
fill: {
opacity: 0.7,
},
tooltip: {
theme: 'dark',
fillSeriesColor: true,
},
legend: {
position: 'right',
},
grid: {
padding: {
left: 0,
right: 0,
top: 0,
bottom: 0,
},
},
}
}
radar() {
this.opts.yaxis[0].labels.offsetY = this.opts.yaxis[0].labels.offsetY
? this.opts.yaxis[0].labels.offsetY
: 6
return {
dataLabels: {
enabled: false,
style: {
fontSize: '11px',
},
},
stroke: {
width: 2,
},
markers: {
size: 5,
strokeWidth: 1,
strokeOpacity: 1,
},
fill: {
opacity: 0.2,
},
tooltip: {
shared: false,
intersect: true,
followCursor: true,
},
grid: {
show: false,
padding: {
left: 0,
right: 0,
top: 0,
bottom: 0,
},
},
xaxis: {
labels: {
formatter: (/** @type {any} */ val) => val,
style: {
colors: ['#a8a8a8'],
fontSize: '11px',
},
},
tooltip: {
enabled: false,
},
crosshairs: {
show: false,
},
},
}
}
radialBar() {
return {
chart: {
animations: {
dynamicAnimation: {
enabled: true,
speed: 800,
},
},
toolbar: {
show: false,
},
},
fill: {
gradient: {
shade: 'dark',
shadeIntensity: 0.4,
inverseColors: false,
type: 'diagonal2',
opacityFrom: 1,
opacityTo: 1,
stops: [70, 98, 100],
},
},
legend: {
show: false,
position: 'right',
},
tooltip: {
enabled: false,
fillSeriesColor: true,
},
grid: {
padding: {
left: 0,
right: 0,
top: 0,
bottom: 0,
},
},
}
}
/**
* @param {import('../../types/internal').ChartStateW} w
* @param {number} seriesIndex
* @param {number} dataPointIndex
* @param {any[]} labels
* @param {string} chartType
*/
_getBoxTooltip(w, seriesIndex, dataPointIndex, labels, chartType) {
const o = w.candleData.seriesCandleO[seriesIndex][dataPointIndex]
const h = w.candleData.seriesCandleH[seriesIndex][dataPointIndex]
const m = w.candleData.seriesCandleM[seriesIndex][dataPointIndex]
const l = w.candleData.seriesCandleL[seriesIndex][dataPointIndex]
const c = w.candleData.seriesCandleC[seriesIndex][dataPointIndex]
const _si = /** @type {Record<string,any>} */ (w.config.series[seriesIndex])
if (_si.type && _si.type !== chartType) {
return `<div class="apexcharts-custom-tooltip">
${
_si.name ? _si.name : 'series-' + (seriesIndex + 1)
}: <strong>${w.seriesData.series[seriesIndex][dataPointIndex]}</strong>
</div>`
} else {
return (
`<div class="apexcharts-tooltip-box apexcharts-tooltip-${w.config.chart.type}">` +
`<div>${labels[0]}: <span class="value">` +
o +
'</span></div>' +
`<div>${labels[1]}: <span class="value">` +
h +
'</span></div>' +
(m
? `<div>${labels[2]}: <span class="value">` + m + '</span></div>'
: '') +
`<div>${labels[3]}: <span class="value">` +
l +
'</span></div>' +
`<div>${labels[4]}: <span class="value">` +
c +
'</span></div>' +
'</div>'
)
}
}
}