feat:Added piechart in Dashboard
This commit is contained in:
+193
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* ApexCharts Tooltip.AxesTooltip Class.
|
||||
* This file deals with the x-axis and y-axis tooltips.
|
||||
*
|
||||
* @module Tooltip.AxesTooltip
|
||||
**/
|
||||
|
||||
class AxesTooltip {
|
||||
constructor(tooltipContext) {
|
||||
this.w = tooltipContext.w
|
||||
this.ttCtx = tooltipContext
|
||||
}
|
||||
|
||||
/**
|
||||
* This method adds the secondary tooltip which appears below x axis
|
||||
* @memberof Tooltip
|
||||
**/
|
||||
drawXaxisTooltip() {
|
||||
let w = this.w
|
||||
const ttCtx = this.ttCtx
|
||||
|
||||
const isBottom = w.config.xaxis.position === 'bottom'
|
||||
|
||||
ttCtx.xaxisOffY = isBottom
|
||||
? w.globals.gridHeight + 1
|
||||
: -w.globals.xAxisHeight - w.config.xaxis.axisTicks.height + 3
|
||||
const tooltipCssClass = isBottom
|
||||
? 'apexcharts-xaxistooltip apexcharts-xaxistooltip-bottom'
|
||||
: 'apexcharts-xaxistooltip apexcharts-xaxistooltip-top'
|
||||
|
||||
let renderTo = w.globals.dom.elWrap
|
||||
|
||||
if (ttCtx.isXAxisTooltipEnabled) {
|
||||
let xaxisTooltip = w.globals.dom.baseEl.querySelector(
|
||||
'.apexcharts-xaxistooltip'
|
||||
)
|
||||
|
||||
if (xaxisTooltip === null) {
|
||||
ttCtx.xaxisTooltip = document.createElement('div')
|
||||
ttCtx.xaxisTooltip.setAttribute(
|
||||
'class',
|
||||
tooltipCssClass + ' apexcharts-theme-' + w.config.tooltip.theme
|
||||
)
|
||||
|
||||
renderTo.appendChild(ttCtx.xaxisTooltip)
|
||||
|
||||
ttCtx.xaxisTooltipText = document.createElement('div')
|
||||
ttCtx.xaxisTooltipText.classList.add('apexcharts-xaxistooltip-text')
|
||||
|
||||
ttCtx.xaxisTooltipText.style.fontFamily =
|
||||
w.config.xaxis.tooltip.style.fontFamily || w.config.chart.fontFamily
|
||||
ttCtx.xaxisTooltipText.style.fontSize =
|
||||
w.config.xaxis.tooltip.style.fontSize
|
||||
|
||||
ttCtx.xaxisTooltip.appendChild(ttCtx.xaxisTooltipText)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method adds the secondary tooltip which appears below x axis
|
||||
* @memberof Tooltip
|
||||
**/
|
||||
drawYaxisTooltip() {
|
||||
let w = this.w
|
||||
const ttCtx = this.ttCtx
|
||||
|
||||
for (let i = 0; i < w.config.yaxis.length; i++) {
|
||||
const isRight =
|
||||
w.config.yaxis[i].opposite || w.config.yaxis[i].crosshairs.opposite
|
||||
|
||||
ttCtx.yaxisOffX = isRight ? w.globals.gridWidth + 1 : 1
|
||||
let tooltipCssClass = isRight
|
||||
? `apexcharts-yaxistooltip apexcharts-yaxistooltip-${i} apexcharts-yaxistooltip-right`
|
||||
: `apexcharts-yaxistooltip apexcharts-yaxistooltip-${i} apexcharts-yaxistooltip-left`
|
||||
|
||||
w.globals.yAxisSameScaleIndices.map((samescales, ssi) => {
|
||||
samescales.map((s, si) => {
|
||||
if (si === i) {
|
||||
tooltipCssClass += w.config.yaxis[si].show
|
||||
? ` `
|
||||
: ` apexcharts-yaxistooltip-hidden`
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
let renderTo = w.globals.dom.elWrap
|
||||
|
||||
let yaxisTooltip = w.globals.dom.baseEl.querySelector(
|
||||
`.apexcharts-yaxistooltip apexcharts-yaxistooltip-${i}`
|
||||
)
|
||||
|
||||
if (yaxisTooltip === null) {
|
||||
ttCtx.yaxisTooltip = document.createElement('div')
|
||||
ttCtx.yaxisTooltip.setAttribute(
|
||||
'class',
|
||||
tooltipCssClass + ' apexcharts-theme-' + w.config.tooltip.theme
|
||||
)
|
||||
|
||||
renderTo.appendChild(ttCtx.yaxisTooltip)
|
||||
|
||||
if (i === 0) ttCtx.yaxisTooltipText = []
|
||||
|
||||
ttCtx.yaxisTooltipText[i] = document.createElement('div')
|
||||
ttCtx.yaxisTooltipText[i].classList.add('apexcharts-yaxistooltip-text')
|
||||
|
||||
ttCtx.yaxisTooltip.appendChild(ttCtx.yaxisTooltipText[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @memberof Tooltip
|
||||
**/
|
||||
setXCrosshairWidth() {
|
||||
let w = this.w
|
||||
const ttCtx = this.ttCtx
|
||||
|
||||
// set xcrosshairs width
|
||||
const xcrosshairs = ttCtx.getElXCrosshairs()
|
||||
ttCtx.xcrosshairsWidth = parseInt(w.config.xaxis.crosshairs.width, 10)
|
||||
|
||||
if (!w.globals.comboCharts) {
|
||||
if (w.config.xaxis.crosshairs.width === 'tickWidth') {
|
||||
let count = w.globals.labels.length
|
||||
ttCtx.xcrosshairsWidth = w.globals.gridWidth / count
|
||||
} else if (w.config.xaxis.crosshairs.width === 'barWidth') {
|
||||
let bar = w.globals.dom.baseEl.querySelector('.apexcharts-bar-area')
|
||||
if (bar !== null) {
|
||||
let barWidth = parseFloat(bar.getAttribute('barWidth'))
|
||||
ttCtx.xcrosshairsWidth = barWidth
|
||||
} else {
|
||||
ttCtx.xcrosshairsWidth = 1
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let bar = w.globals.dom.baseEl.querySelector('.apexcharts-bar-area')
|
||||
if (bar !== null && w.config.xaxis.crosshairs.width === 'barWidth') {
|
||||
let barWidth = parseFloat(bar.getAttribute('barWidth'))
|
||||
ttCtx.xcrosshairsWidth = barWidth
|
||||
} else {
|
||||
if (w.config.xaxis.crosshairs.width === 'tickWidth') {
|
||||
let count = w.globals.labels.length
|
||||
ttCtx.xcrosshairsWidth = w.globals.gridWidth / count
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (w.globals.isBarHorizontal) {
|
||||
ttCtx.xcrosshairsWidth = 0
|
||||
}
|
||||
if (xcrosshairs !== null && ttCtx.xcrosshairsWidth > 0) {
|
||||
xcrosshairs.setAttribute('width', ttCtx.xcrosshairsWidth)
|
||||
}
|
||||
}
|
||||
|
||||
handleYCrosshair() {
|
||||
let w = this.w
|
||||
const ttCtx = this.ttCtx
|
||||
|
||||
// set ycrosshairs height
|
||||
ttCtx.ycrosshairs = w.globals.dom.baseEl.querySelector(
|
||||
'.apexcharts-ycrosshairs'
|
||||
)
|
||||
|
||||
ttCtx.ycrosshairsHidden = w.globals.dom.baseEl.querySelector(
|
||||
'.apexcharts-ycrosshairs-hidden'
|
||||
)
|
||||
}
|
||||
|
||||
drawYaxisTooltipText(index, clientY, xyRatios) {
|
||||
const ttCtx = this.ttCtx
|
||||
const w = this.w
|
||||
|
||||
let lbFormatter = w.globals.yLabelFormatters[index]
|
||||
|
||||
if (ttCtx.yaxisTooltips[index]) {
|
||||
const elGrid = ttCtx.getElGrid()
|
||||
const seriesBound = elGrid.getBoundingClientRect()
|
||||
|
||||
const hoverY = (clientY - seriesBound.top) * xyRatios.yRatio[index]
|
||||
const height = w.globals.maxYArr[index] - w.globals.minYArr[index]
|
||||
|
||||
const val = w.globals.minYArr[index] + (height - hoverY)
|
||||
|
||||
ttCtx.tooltipPosition.moveYCrosshairs(clientY - seriesBound.top)
|
||||
ttCtx.yaxisTooltipText[index].innerHTML = lbFormatter(val)
|
||||
ttCtx.tooltipPosition.moveYAxisTooltip(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default AxesTooltip
|
||||
+366
@@ -0,0 +1,366 @@
|
||||
import Utils from '../../utils/Utils'
|
||||
|
||||
/**
|
||||
* ApexCharts Tooltip.Intersect Class.
|
||||
* This file deals with functions related to intersecting tooltips
|
||||
* (tooltips that appear when user hovers directly over a data-point whether)
|
||||
*
|
||||
* @module Tooltip.Intersect
|
||||
**/
|
||||
|
||||
class Intersect {
|
||||
constructor(tooltipContext) {
|
||||
this.w = tooltipContext.w
|
||||
const w = this.w
|
||||
this.ttCtx = tooltipContext
|
||||
|
||||
this.isVerticalGroupedRangeBar =
|
||||
!w.globals.isBarHorizontal &&
|
||||
w.config.chart.type === 'rangeBar' &&
|
||||
w.config.plotOptions.bar.rangeBarGroupRows
|
||||
}
|
||||
|
||||
// a helper function to get an element's attribute value
|
||||
getAttr(e, attr) {
|
||||
return parseFloat(e.target.getAttribute(attr))
|
||||
}
|
||||
|
||||
// handle tooltip for heatmaps and treemaps
|
||||
handleHeatTreeTooltip({ e, opt, x, y, type }) {
|
||||
const ttCtx = this.ttCtx
|
||||
const w = this.w
|
||||
|
||||
if (e.target.classList.contains(`apexcharts-${type}-rect`)) {
|
||||
let i = this.getAttr(e, 'i')
|
||||
let j = this.getAttr(e, 'j')
|
||||
let cx = this.getAttr(e, 'cx')
|
||||
let cy = this.getAttr(e, 'cy')
|
||||
let width = this.getAttr(e, 'width')
|
||||
let height = this.getAttr(e, 'height')
|
||||
|
||||
ttCtx.tooltipLabels.drawSeriesTexts({
|
||||
ttItems: opt.ttItems,
|
||||
i,
|
||||
j,
|
||||
shared: false,
|
||||
e,
|
||||
})
|
||||
|
||||
w.globals.capturedSeriesIndex = i
|
||||
w.globals.capturedDataPointIndex = j
|
||||
|
||||
x = cx + ttCtx.tooltipRect.ttWidth / 2 + width
|
||||
y = cy + ttCtx.tooltipRect.ttHeight / 2 - height / 2
|
||||
|
||||
ttCtx.tooltipPosition.moveXCrosshairs(cx + width / 2)
|
||||
|
||||
if (x > w.globals.gridWidth / 2) {
|
||||
x = cx - ttCtx.tooltipRect.ttWidth / 2 + width
|
||||
}
|
||||
if (ttCtx.w.config.tooltip.followCursor) {
|
||||
let seriesBound = w.globals.dom.elWrap.getBoundingClientRect()
|
||||
x =
|
||||
w.globals.clientX -
|
||||
seriesBound.left -
|
||||
(x > w.globals.gridWidth / 2 ? ttCtx.tooltipRect.ttWidth : 0)
|
||||
y =
|
||||
w.globals.clientY -
|
||||
seriesBound.top -
|
||||
(y > w.globals.gridHeight / 2 ? ttCtx.tooltipRect.ttHeight : 0)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* handle tooltips for line/area/scatter charts where tooltip.intersect is true
|
||||
* when user hovers over the marker directly, this function is executed
|
||||
*/
|
||||
handleMarkerTooltip({ e, opt, x, y }) {
|
||||
let w = this.w
|
||||
const ttCtx = this.ttCtx
|
||||
|
||||
let i
|
||||
let j
|
||||
if (e.target.classList.contains('apexcharts-marker')) {
|
||||
let cx = parseInt(opt.paths.getAttribute('cx'), 10)
|
||||
let cy = parseInt(opt.paths.getAttribute('cy'), 10)
|
||||
let val = parseFloat(opt.paths.getAttribute('val'))
|
||||
|
||||
j = parseInt(opt.paths.getAttribute('rel'), 10)
|
||||
i =
|
||||
parseInt(
|
||||
opt.paths.parentNode.parentNode.parentNode.getAttribute('rel'),
|
||||
10
|
||||
) - 1
|
||||
|
||||
if (ttCtx.intersect) {
|
||||
const el = Utils.findAncestor(opt.paths, 'apexcharts-series')
|
||||
if (el) {
|
||||
i = parseInt(el.getAttribute('data:realIndex'), 10)
|
||||
}
|
||||
}
|
||||
|
||||
ttCtx.tooltipLabels.drawSeriesTexts({
|
||||
ttItems: opt.ttItems,
|
||||
i,
|
||||
j,
|
||||
shared: ttCtx.showOnIntersect ? false : w.config.tooltip.shared,
|
||||
e,
|
||||
})
|
||||
|
||||
if (e.type === 'mouseup') {
|
||||
ttCtx.markerClick(e, i, j)
|
||||
}
|
||||
|
||||
w.globals.capturedSeriesIndex = i
|
||||
w.globals.capturedDataPointIndex = j
|
||||
|
||||
x = cx
|
||||
y = cy + w.globals.translateY - ttCtx.tooltipRect.ttHeight * 1.4
|
||||
|
||||
if (ttCtx.w.config.tooltip.followCursor) {
|
||||
const elGrid = ttCtx.getElGrid()
|
||||
const seriesBound = elGrid.getBoundingClientRect()
|
||||
y = ttCtx.e.clientY + w.globals.translateY - seriesBound.top
|
||||
}
|
||||
|
||||
if (val < 0) {
|
||||
y = cy
|
||||
}
|
||||
ttCtx.marker.enlargeCurrentPoint(j, opt.paths, x, y)
|
||||
}
|
||||
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* handle tooltips for bar/column charts
|
||||
*/
|
||||
handleBarTooltip({ e, opt }) {
|
||||
const w = this.w
|
||||
const ttCtx = this.ttCtx
|
||||
|
||||
const tooltipEl = ttCtx.getElTooltip()
|
||||
|
||||
let bx = 0
|
||||
let x = 0
|
||||
let y = 0
|
||||
let i = 0
|
||||
let strokeWidth
|
||||
let barXY = this.getBarTooltipXY({
|
||||
e,
|
||||
opt,
|
||||
})
|
||||
i = barXY.i
|
||||
let barHeight = barXY.barHeight
|
||||
let j = barXY.j
|
||||
|
||||
w.globals.capturedSeriesIndex = i
|
||||
w.globals.capturedDataPointIndex = j
|
||||
|
||||
if (
|
||||
(w.globals.isBarHorizontal && ttCtx.tooltipUtil.hasBars()) ||
|
||||
!w.config.tooltip.shared
|
||||
) {
|
||||
x = barXY.x
|
||||
y = barXY.y
|
||||
strokeWidth = Array.isArray(w.config.stroke.width)
|
||||
? w.config.stroke.width[i]
|
||||
: w.config.stroke.width
|
||||
bx = x
|
||||
} else {
|
||||
if (!w.globals.comboCharts && !w.config.tooltip.shared) {
|
||||
// todo: re-check this condition as it's always 0
|
||||
bx = bx / 2
|
||||
}
|
||||
}
|
||||
|
||||
// y is NaN, make it touch the bottom of grid area
|
||||
if (isNaN(y)) {
|
||||
y = w.globals.svgHeight - ttCtx.tooltipRect.ttHeight
|
||||
}
|
||||
|
||||
const seriesIndex = parseInt(
|
||||
opt.paths.parentNode.getAttribute('data:realIndex'),
|
||||
10
|
||||
)
|
||||
|
||||
const isReversed = w.globals.isMultipleYAxis
|
||||
? w.config.yaxis[seriesIndex] && w.config.yaxis[seriesIndex].reversed
|
||||
: w.config.yaxis[0].reversed
|
||||
|
||||
if (x + ttCtx.tooltipRect.ttWidth > w.globals.gridWidth && !isReversed) {
|
||||
x = x - ttCtx.tooltipRect.ttWidth
|
||||
} else if (x < 0) {
|
||||
x = 0
|
||||
}
|
||||
|
||||
if (ttCtx.w.config.tooltip.followCursor) {
|
||||
const elGrid = ttCtx.getElGrid()
|
||||
const seriesBound = elGrid.getBoundingClientRect()
|
||||
y = ttCtx.e.clientY - seriesBound.top
|
||||
}
|
||||
|
||||
// if tooltip is still null, querySelector
|
||||
if (ttCtx.tooltip === null) {
|
||||
ttCtx.tooltip = w.globals.dom.baseEl.querySelector('.apexcharts-tooltip')
|
||||
}
|
||||
|
||||
if (!w.config.tooltip.shared) {
|
||||
if (w.globals.comboBarCount > 0) {
|
||||
ttCtx.tooltipPosition.moveXCrosshairs(bx + strokeWidth / 2)
|
||||
} else {
|
||||
ttCtx.tooltipPosition.moveXCrosshairs(bx)
|
||||
}
|
||||
}
|
||||
|
||||
// move tooltip here
|
||||
if (
|
||||
!ttCtx.fixedTooltip &&
|
||||
(!w.config.tooltip.shared ||
|
||||
(w.globals.isBarHorizontal && ttCtx.tooltipUtil.hasBars()))
|
||||
) {
|
||||
if (isReversed) {
|
||||
x = x - ttCtx.tooltipRect.ttWidth
|
||||
if (x < 0) {
|
||||
x = 0
|
||||
}
|
||||
}
|
||||
if (
|
||||
isReversed &&
|
||||
!(w.globals.isBarHorizontal && ttCtx.tooltipUtil.hasBars())
|
||||
) {
|
||||
y = y + barHeight - (w.globals.series[i][j] < 0 ? barHeight : 0) * 2
|
||||
}
|
||||
|
||||
y = y + w.globals.translateY - ttCtx.tooltipRect.ttHeight / 2
|
||||
|
||||
tooltipEl.style.left = x + w.globals.translateX + 'px'
|
||||
tooltipEl.style.top = y + 'px'
|
||||
}
|
||||
}
|
||||
|
||||
getBarTooltipXY({ e, opt }) {
|
||||
let w = this.w
|
||||
let j = null
|
||||
const ttCtx = this.ttCtx
|
||||
let i = 0
|
||||
let x = 0
|
||||
let y = 0
|
||||
let barWidth = 0
|
||||
let barHeight = 0
|
||||
|
||||
const cl = e.target.classList
|
||||
|
||||
if (
|
||||
cl.contains('apexcharts-bar-area') ||
|
||||
cl.contains('apexcharts-candlestick-area') ||
|
||||
cl.contains('apexcharts-boxPlot-area') ||
|
||||
cl.contains('apexcharts-rangebar-area')
|
||||
) {
|
||||
let bar = e.target
|
||||
let barRect = bar.getBoundingClientRect()
|
||||
|
||||
let seriesBound = opt.elGrid.getBoundingClientRect()
|
||||
|
||||
let bh = barRect.height
|
||||
barHeight = barRect.height
|
||||
let bw = barRect.width
|
||||
|
||||
let cx = parseInt(bar.getAttribute('cx'), 10)
|
||||
let cy = parseInt(bar.getAttribute('cy'), 10)
|
||||
barWidth = parseFloat(bar.getAttribute('barWidth'))
|
||||
const clientX = e.type === 'touchmove' ? e.touches[0].clientX : e.clientX
|
||||
|
||||
j = parseInt(bar.getAttribute('j'), 10)
|
||||
i = parseInt(bar.parentNode.getAttribute('rel'), 10) - 1
|
||||
|
||||
let y1 = bar.getAttribute('data-range-y1')
|
||||
let y2 = bar.getAttribute('data-range-y2')
|
||||
|
||||
if (w.globals.comboCharts) {
|
||||
i = parseInt(bar.parentNode.getAttribute('data:realIndex'), 10)
|
||||
}
|
||||
|
||||
// if (w.config.tooltip.shared) {
|
||||
// this check not needed at the moment
|
||||
// const yDivisor = w.globals.gridHeight / (w.globals.series.length)
|
||||
// const hoverY = ttCtx.clientY - ttCtx.seriesBound.top
|
||||
|
||||
// j = Math.ceil(hoverY / yDivisor)
|
||||
// }
|
||||
|
||||
const handleXForColumns = (x) => {
|
||||
if (w.globals.isXNumeric) {
|
||||
x = cx - bw / 2
|
||||
} else {
|
||||
if (this.isVerticalGroupedRangeBar) {
|
||||
x = cx + bw / 2
|
||||
} else {
|
||||
x = cx - ttCtx.dataPointsDividedWidth + bw / 2
|
||||
}
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
const handleYForBars = () => {
|
||||
return (
|
||||
cy -
|
||||
ttCtx.dataPointsDividedHeight +
|
||||
bh / 2 -
|
||||
ttCtx.tooltipRect.ttHeight / 2
|
||||
)
|
||||
}
|
||||
|
||||
ttCtx.tooltipLabels.drawSeriesTexts({
|
||||
ttItems: opt.ttItems,
|
||||
i,
|
||||
j,
|
||||
y1: y1 ? parseInt(y1, 10) : null,
|
||||
y2: y2 ? parseInt(y2, 10) : null,
|
||||
shared: ttCtx.showOnIntersect ? false : w.config.tooltip.shared,
|
||||
e,
|
||||
})
|
||||
|
||||
if (w.config.tooltip.followCursor) {
|
||||
if (w.globals.isBarHorizontal) {
|
||||
x = clientX - seriesBound.left + 15
|
||||
y = handleYForBars()
|
||||
} else {
|
||||
x = handleXForColumns(x)
|
||||
y = e.clientY - seriesBound.top - ttCtx.tooltipRect.ttHeight / 2 - 15
|
||||
}
|
||||
} else {
|
||||
if (w.globals.isBarHorizontal) {
|
||||
x = cx
|
||||
if (x < ttCtx.xyRatios.baseLineInvertedY) {
|
||||
x = cx - ttCtx.tooltipRect.ttWidth
|
||||
}
|
||||
y = handleYForBars()
|
||||
} else {
|
||||
x = handleXForColumns(x)
|
||||
y = cy // - ttCtx.tooltipRect.ttHeight / 2 + 10
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
barHeight,
|
||||
barWidth,
|
||||
i,
|
||||
j,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Intersect
|
||||
+512
@@ -0,0 +1,512 @@
|
||||
import Formatters from '../Formatters'
|
||||
import DateTime from '../../utils/DateTime'
|
||||
import Utils from './Utils'
|
||||
|
||||
/**
|
||||
* ApexCharts Tooltip.Labels Class to draw texts on the tooltip.
|
||||
* This file deals with printing actual text on the tooltip.
|
||||
*
|
||||
* @module Tooltip.Labels
|
||||
**/
|
||||
|
||||
export default class Labels {
|
||||
constructor(tooltipContext) {
|
||||
this.w = tooltipContext.w
|
||||
this.ctx = tooltipContext.ctx
|
||||
this.ttCtx = tooltipContext
|
||||
this.tooltipUtil = new Utils(tooltipContext)
|
||||
}
|
||||
|
||||
drawSeriesTexts({ shared = true, ttItems, i = 0, j = null, y1, y2, e }) {
|
||||
let w = this.w
|
||||
|
||||
if (w.config.tooltip.custom !== undefined) {
|
||||
this.handleCustomTooltip({ i, j, y1, y2, w })
|
||||
} else {
|
||||
this.toggleActiveInactiveSeries(shared)
|
||||
}
|
||||
|
||||
let values = this.getValuesToPrint({
|
||||
i,
|
||||
j,
|
||||
})
|
||||
|
||||
this.printLabels({
|
||||
i,
|
||||
j,
|
||||
values,
|
||||
ttItems,
|
||||
shared,
|
||||
e,
|
||||
})
|
||||
|
||||
// Re-calculate tooltip dimensions now that we have drawn the text
|
||||
const tooltipEl = this.ttCtx.getElTooltip()
|
||||
|
||||
this.ttCtx.tooltipRect.ttWidth = tooltipEl.getBoundingClientRect().width
|
||||
this.ttCtx.tooltipRect.ttHeight = tooltipEl.getBoundingClientRect().height
|
||||
}
|
||||
|
||||
printLabels({ i, j, values, ttItems, shared, e }) {
|
||||
const w = this.w
|
||||
let val
|
||||
let goalVals = []
|
||||
const hasGoalValues = (gi) => {
|
||||
return (
|
||||
w.globals.seriesGoals[gi] &&
|
||||
w.globals.seriesGoals[gi][j] &&
|
||||
Array.isArray(w.globals.seriesGoals[gi][j])
|
||||
)
|
||||
}
|
||||
|
||||
const { xVal, zVal, xAxisTTVal } = values
|
||||
|
||||
let seriesName = ''
|
||||
|
||||
let pColor = w.globals.colors[i] // The pColor here is for the markers inside tooltip
|
||||
if (j !== null && w.config.plotOptions.bar.distributed) {
|
||||
pColor = w.globals.colors[j]
|
||||
}
|
||||
|
||||
for (
|
||||
let t = 0, inverset = w.globals.series.length - 1;
|
||||
t < w.globals.series.length;
|
||||
t++, inverset--
|
||||
) {
|
||||
let f = this.getFormatters(i)
|
||||
seriesName = this.getSeriesName({
|
||||
fn: f.yLbTitleFormatter,
|
||||
index: i,
|
||||
seriesIndex: i,
|
||||
j,
|
||||
})
|
||||
|
||||
if (w.config.chart.type === 'treemap') {
|
||||
seriesName = f.yLbTitleFormatter(String(w.config.series[i].data[j].x), {
|
||||
series: w.globals.series,
|
||||
seriesIndex: i,
|
||||
dataPointIndex: j,
|
||||
w,
|
||||
})
|
||||
}
|
||||
|
||||
const tIndex = w.config.tooltip.inverseOrder ? inverset : t
|
||||
|
||||
if (w.globals.axisCharts) {
|
||||
const getValBySeriesIndex = (index) => {
|
||||
if (w.globals.isRangeData) {
|
||||
return (
|
||||
f.yLbFormatter(w.globals.seriesRangeStart?.[index]?.[j], {
|
||||
series: w.globals.seriesRangeStart,
|
||||
seriesIndex: index,
|
||||
dataPointIndex: j,
|
||||
w,
|
||||
}) +
|
||||
' - ' +
|
||||
f.yLbFormatter(w.globals.seriesRangeEnd?.[index]?.[j], {
|
||||
series: w.globals.seriesRangeEnd,
|
||||
seriesIndex: index,
|
||||
dataPointIndex: j,
|
||||
w,
|
||||
})
|
||||
)
|
||||
}
|
||||
return f.yLbFormatter(w.globals.series[index][j], {
|
||||
series: w.globals.series,
|
||||
seriesIndex: index,
|
||||
dataPointIndex: j,
|
||||
w,
|
||||
})
|
||||
}
|
||||
if (shared) {
|
||||
f = this.getFormatters(tIndex)
|
||||
|
||||
seriesName = this.getSeriesName({
|
||||
fn: f.yLbTitleFormatter,
|
||||
index: tIndex,
|
||||
seriesIndex: i,
|
||||
j,
|
||||
})
|
||||
pColor = w.globals.colors[tIndex]
|
||||
|
||||
val = getValBySeriesIndex(tIndex)
|
||||
if (hasGoalValues(tIndex)) {
|
||||
goalVals = w.globals.seriesGoals[tIndex][j].map((goal) => {
|
||||
return {
|
||||
attrs: goal,
|
||||
val: f.yLbFormatter(goal.value, {
|
||||
seriesIndex: tIndex,
|
||||
dataPointIndex: j,
|
||||
w,
|
||||
}),
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// get a color from a hover area (if it's a line pattern then get from a first line)
|
||||
const targetFill = e?.target?.getAttribute('fill')
|
||||
if (targetFill) {
|
||||
pColor =
|
||||
targetFill.indexOf('url') !== -1
|
||||
? document
|
||||
.querySelector(targetFill.substr(4).slice(0, -1))
|
||||
.childNodes[0].getAttribute('stroke')
|
||||
: targetFill
|
||||
}
|
||||
val = getValBySeriesIndex(i)
|
||||
if (hasGoalValues(i) && Array.isArray(w.globals.seriesGoals[i][j])) {
|
||||
goalVals = w.globals.seriesGoals[i][j].map((goal) => {
|
||||
return {
|
||||
attrs: goal,
|
||||
val: f.yLbFormatter(goal.value, {
|
||||
seriesIndex: i,
|
||||
dataPointIndex: j,
|
||||
w,
|
||||
}),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for pie / donuts
|
||||
if (j === null) {
|
||||
val = f.yLbFormatter(w.globals.series[i], {
|
||||
...w,
|
||||
seriesIndex: i,
|
||||
dataPointIndex: i,
|
||||
})
|
||||
}
|
||||
|
||||
this.DOMHandling({
|
||||
i,
|
||||
t: tIndex,
|
||||
j,
|
||||
ttItems,
|
||||
values: {
|
||||
val,
|
||||
goalVals,
|
||||
xVal,
|
||||
xAxisTTVal,
|
||||
zVal,
|
||||
},
|
||||
seriesName,
|
||||
shared,
|
||||
pColor,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
getFormatters(i) {
|
||||
const w = this.w
|
||||
|
||||
let yLbFormatter = w.globals.yLabelFormatters[i]
|
||||
let yLbTitleFormatter
|
||||
|
||||
if (w.globals.ttVal !== undefined) {
|
||||
if (Array.isArray(w.globals.ttVal)) {
|
||||
yLbFormatter = w.globals.ttVal[i] && w.globals.ttVal[i].formatter
|
||||
yLbTitleFormatter =
|
||||
w.globals.ttVal[i] &&
|
||||
w.globals.ttVal[i].title &&
|
||||
w.globals.ttVal[i].title.formatter
|
||||
} else {
|
||||
yLbFormatter = w.globals.ttVal.formatter
|
||||
if (typeof w.globals.ttVal.title.formatter === 'function') {
|
||||
yLbTitleFormatter = w.globals.ttVal.title.formatter
|
||||
}
|
||||
}
|
||||
} else {
|
||||
yLbTitleFormatter = w.config.tooltip.y.title.formatter
|
||||
}
|
||||
|
||||
if (typeof yLbFormatter !== 'function') {
|
||||
if (w.globals.yLabelFormatters[0]) {
|
||||
yLbFormatter = w.globals.yLabelFormatters[0]
|
||||
} else {
|
||||
yLbFormatter = function (label) {
|
||||
return label
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof yLbTitleFormatter !== 'function') {
|
||||
yLbTitleFormatter = function (label) {
|
||||
return label
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
yLbFormatter,
|
||||
yLbTitleFormatter,
|
||||
}
|
||||
}
|
||||
|
||||
getSeriesName({ fn, index, seriesIndex, j }) {
|
||||
const w = this.w
|
||||
return fn(String(w.globals.seriesNames[index]), {
|
||||
series: w.globals.series,
|
||||
seriesIndex,
|
||||
dataPointIndex: j,
|
||||
w,
|
||||
})
|
||||
}
|
||||
|
||||
DOMHandling({ i, t, j, ttItems, values, seriesName, shared, pColor }) {
|
||||
const w = this.w
|
||||
const ttCtx = this.ttCtx
|
||||
|
||||
const { val, goalVals, xVal, xAxisTTVal, zVal } = values
|
||||
|
||||
let ttItemsChildren = null
|
||||
ttItemsChildren = ttItems[t].children
|
||||
|
||||
if (w.config.tooltip.fillSeriesColor) {
|
||||
ttItems[t].style.backgroundColor = pColor
|
||||
ttItemsChildren[0].style.display = 'none'
|
||||
}
|
||||
|
||||
if (ttCtx.showTooltipTitle) {
|
||||
if (ttCtx.tooltipTitle === null) {
|
||||
// get it once if null, and store it in class property
|
||||
ttCtx.tooltipTitle = w.globals.dom.baseEl.querySelector(
|
||||
'.apexcharts-tooltip-title'
|
||||
)
|
||||
}
|
||||
ttCtx.tooltipTitle.innerHTML = xVal
|
||||
}
|
||||
|
||||
// if xaxis tooltip is constructed, we need to replace the innerHTML
|
||||
if (ttCtx.isXAxisTooltipEnabled) {
|
||||
ttCtx.xaxisTooltipText.innerHTML = xAxisTTVal !== '' ? xAxisTTVal : xVal
|
||||
}
|
||||
|
||||
const ttYLabel = ttItems[t].querySelector(
|
||||
'.apexcharts-tooltip-text-y-label'
|
||||
)
|
||||
if (ttYLabel) {
|
||||
ttYLabel.innerHTML = seriesName ? seriesName : ''
|
||||
}
|
||||
const ttYVal = ttItems[t].querySelector('.apexcharts-tooltip-text-y-value')
|
||||
if (ttYVal) {
|
||||
ttYVal.innerHTML = typeof val !== 'undefined' ? val : ''
|
||||
}
|
||||
|
||||
if (
|
||||
ttItemsChildren[0] &&
|
||||
ttItemsChildren[0].classList.contains('apexcharts-tooltip-marker')
|
||||
) {
|
||||
if (
|
||||
w.config.tooltip.marker.fillColors &&
|
||||
Array.isArray(w.config.tooltip.marker.fillColors)
|
||||
) {
|
||||
pColor = w.config.tooltip.marker.fillColors[t]
|
||||
}
|
||||
|
||||
ttItemsChildren[0].style.backgroundColor = pColor
|
||||
}
|
||||
|
||||
if (!w.config.tooltip.marker.show) {
|
||||
ttItemsChildren[0].style.display = 'none'
|
||||
}
|
||||
|
||||
const ttGLabel = ttItems[t].querySelector(
|
||||
'.apexcharts-tooltip-text-goals-label'
|
||||
)
|
||||
const ttGVal = ttItems[t].querySelector(
|
||||
'.apexcharts-tooltip-text-goals-value'
|
||||
)
|
||||
|
||||
if (goalVals.length && w.globals.seriesGoals[t]) {
|
||||
const createGoalsHtml = () => {
|
||||
let gLabels = '<div >'
|
||||
let gVals = '<div>'
|
||||
goalVals.forEach((goal, gi) => {
|
||||
gLabels += ` <div style="display: flex"><span class="apexcharts-tooltip-marker" style="background-color: ${goal.attrs.strokeColor}; height: 3px; border-radius: 0; top: 5px;"></span> ${goal.attrs.name}</div>`
|
||||
gVals += `<div>${goal.val}</div>`
|
||||
})
|
||||
ttGLabel.innerHTML = gLabels + `</div>`
|
||||
ttGVal.innerHTML = gVals + `</div>`
|
||||
}
|
||||
if (shared) {
|
||||
if (
|
||||
w.globals.seriesGoals[t][j] &&
|
||||
Array.isArray(w.globals.seriesGoals[t][j])
|
||||
) {
|
||||
createGoalsHtml()
|
||||
} else {
|
||||
ttGLabel.innerHTML = ''
|
||||
ttGVal.innerHTML = ''
|
||||
}
|
||||
} else {
|
||||
createGoalsHtml()
|
||||
}
|
||||
} else {
|
||||
ttGLabel.innerHTML = ''
|
||||
ttGVal.innerHTML = ''
|
||||
}
|
||||
|
||||
if (zVal !== null) {
|
||||
const ttZLabel = ttItems[t].querySelector(
|
||||
'.apexcharts-tooltip-text-z-label'
|
||||
)
|
||||
ttZLabel.innerHTML = w.config.tooltip.z.title
|
||||
const ttZVal = ttItems[t].querySelector(
|
||||
'.apexcharts-tooltip-text-z-value'
|
||||
)
|
||||
ttZVal.innerHTML = typeof zVal !== 'undefined' ? zVal : ''
|
||||
}
|
||||
|
||||
if (shared && ttItemsChildren[0]) {
|
||||
// hide when no Val or series collapsed
|
||||
if (w.config.tooltip.hideEmptySeries) {
|
||||
let ttItemMarker = ttItems[t].querySelector(
|
||||
'.apexcharts-tooltip-marker'
|
||||
)
|
||||
let ttItemText = ttItems[t].querySelector('.apexcharts-tooltip-text')
|
||||
if (parseFloat(val) == 0) {
|
||||
ttItemMarker.style.display = 'none'
|
||||
ttItemText.style.display = 'none'
|
||||
} else {
|
||||
ttItemMarker.style.display = 'block'
|
||||
ttItemText.style.display = 'block'
|
||||
}
|
||||
}
|
||||
if (
|
||||
typeof val === 'undefined' ||
|
||||
val === null ||
|
||||
w.globals.ancillaryCollapsedSeriesIndices.indexOf(t) > -1 ||
|
||||
w.globals.collapsedSeriesIndices.indexOf(t) > -1
|
||||
) {
|
||||
ttItemsChildren[0].parentNode.style.display = 'none'
|
||||
} else {
|
||||
ttItemsChildren[0].parentNode.style.display =
|
||||
w.config.tooltip.items.display
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
toggleActiveInactiveSeries(shared) {
|
||||
const w = this.w
|
||||
if (shared) {
|
||||
// make all tooltips active
|
||||
this.tooltipUtil.toggleAllTooltipSeriesGroups('enable')
|
||||
} else {
|
||||
// disable all tooltip text groups
|
||||
this.tooltipUtil.toggleAllTooltipSeriesGroups('disable')
|
||||
|
||||
// enable the first tooltip text group
|
||||
let firstTooltipSeriesGroup = w.globals.dom.baseEl.querySelector(
|
||||
'.apexcharts-tooltip-series-group'
|
||||
)
|
||||
|
||||
if (firstTooltipSeriesGroup) {
|
||||
firstTooltipSeriesGroup.classList.add('apexcharts-active')
|
||||
firstTooltipSeriesGroup.style.display = w.config.tooltip.items.display
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getValuesToPrint({ i, j }) {
|
||||
const w = this.w
|
||||
const filteredSeriesX = this.ctx.series.filteredSeriesX()
|
||||
|
||||
let xVal = ''
|
||||
let xAxisTTVal = ''
|
||||
let zVal = null
|
||||
let val = null
|
||||
|
||||
const customFormatterOpts = {
|
||||
series: w.globals.series,
|
||||
seriesIndex: i,
|
||||
dataPointIndex: j,
|
||||
w,
|
||||
}
|
||||
|
||||
let zFormatter = w.globals.ttZFormatter
|
||||
|
||||
if (j === null) {
|
||||
val = w.globals.series[i]
|
||||
} else {
|
||||
if (w.globals.isXNumeric && w.config.chart.type !== 'treemap') {
|
||||
xVal = filteredSeriesX[i][j]
|
||||
if (filteredSeriesX[i].length === 0) {
|
||||
// a series (possibly the first one) might be collapsed, so get the next active index
|
||||
const firstActiveSeriesIndex =
|
||||
this.tooltipUtil.getFirstActiveXArray(filteredSeriesX)
|
||||
xVal = filteredSeriesX[firstActiveSeriesIndex][j]
|
||||
}
|
||||
} else {
|
||||
xVal =
|
||||
typeof w.globals.labels[j] !== 'undefined' ? w.globals.labels[j] : ''
|
||||
}
|
||||
}
|
||||
|
||||
let bufferXVal = xVal
|
||||
|
||||
if (w.globals.isXNumeric && w.config.xaxis.type === 'datetime') {
|
||||
let xFormat = new Formatters(this.ctx)
|
||||
xVal = xFormat.xLabelFormat(
|
||||
w.globals.ttKeyFormatter,
|
||||
bufferXVal,
|
||||
bufferXVal,
|
||||
{
|
||||
i: undefined,
|
||||
dateFormatter: new DateTime(this.ctx).formatDate,
|
||||
w: this.w,
|
||||
}
|
||||
)
|
||||
} else {
|
||||
if (w.globals.isBarHorizontal) {
|
||||
xVal = w.globals.yLabelFormatters[0](bufferXVal, customFormatterOpts)
|
||||
} else {
|
||||
xVal = w.globals.xLabelFormatter(bufferXVal, customFormatterOpts)
|
||||
}
|
||||
}
|
||||
|
||||
// override default x-axis formatter with tooltip formatter
|
||||
if (w.config.tooltip.x.formatter !== undefined) {
|
||||
xVal = w.globals.ttKeyFormatter(bufferXVal, customFormatterOpts)
|
||||
}
|
||||
|
||||
if (w.globals.seriesZ.length > 0 && w.globals.seriesZ[i].length > 0) {
|
||||
zVal = zFormatter(w.globals.seriesZ[i][j], w)
|
||||
}
|
||||
|
||||
if (typeof w.config.xaxis.tooltip.formatter === 'function') {
|
||||
xAxisTTVal = w.globals.xaxisTooltipFormatter(
|
||||
bufferXVal,
|
||||
customFormatterOpts
|
||||
)
|
||||
} else {
|
||||
xAxisTTVal = xVal
|
||||
}
|
||||
|
||||
return {
|
||||
val: Array.isArray(val) ? val.join(' ') : val,
|
||||
xVal: Array.isArray(xVal) ? xVal.join(' ') : xVal,
|
||||
xAxisTTVal: Array.isArray(xAxisTTVal) ? xAxisTTVal.join(' ') : xAxisTTVal,
|
||||
zVal,
|
||||
}
|
||||
}
|
||||
|
||||
handleCustomTooltip({ i, j, y1, y2, w }) {
|
||||
const tooltipEl = this.ttCtx.getElTooltip()
|
||||
let fn = w.config.tooltip.custom
|
||||
|
||||
if (Array.isArray(fn) && fn[i]) {
|
||||
fn = fn[i]
|
||||
}
|
||||
|
||||
// override everything with a custom html tooltip and replace it
|
||||
tooltipEl.innerHTML = fn({
|
||||
ctx: this.ctx,
|
||||
series: w.globals.series,
|
||||
seriesIndex: i,
|
||||
dataPointIndex: j,
|
||||
y1,
|
||||
y2,
|
||||
w,
|
||||
})
|
||||
}
|
||||
}
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
import Graphics from '../Graphics'
|
||||
import Position from './Position'
|
||||
import Markers from '../../modules/Markers'
|
||||
import Utils from '../../utils/Utils'
|
||||
|
||||
/**
|
||||
* ApexCharts Tooltip.Marker Class to draw texts on the tooltip.
|
||||
* This file deals with the markers that appear near tooltip in line/area charts.
|
||||
* These markers helps the user to associate the data-points and the values
|
||||
* that are shown in the tooltip
|
||||
*
|
||||
* @module Tooltip.Marker
|
||||
**/
|
||||
|
||||
export default class Marker {
|
||||
constructor(tooltipContext) {
|
||||
this.w = tooltipContext.w
|
||||
this.ttCtx = tooltipContext
|
||||
this.ctx = tooltipContext.ctx
|
||||
this.tooltipPosition = new Position(tooltipContext)
|
||||
}
|
||||
|
||||
drawDynamicPoints() {
|
||||
let w = this.w
|
||||
|
||||
let graphics = new Graphics(this.ctx)
|
||||
let marker = new Markers(this.ctx)
|
||||
|
||||
let elsSeries = w.globals.dom.baseEl.querySelectorAll('.apexcharts-series')
|
||||
|
||||
elsSeries = [...elsSeries]
|
||||
|
||||
if (w.config.chart.stacked) {
|
||||
elsSeries.sort((a, b) => {
|
||||
return (
|
||||
parseFloat(a.getAttribute('data:realIndex')) -
|
||||
parseFloat(b.getAttribute('data:realIndex'))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
for (let i = 0; i < elsSeries.length; i++) {
|
||||
let pointsMain = elsSeries[i].querySelector(
|
||||
`.apexcharts-series-markers-wrap`
|
||||
)
|
||||
|
||||
if (pointsMain !== null) {
|
||||
// it can be null as we have tooltips in donut/bar charts
|
||||
let point
|
||||
|
||||
let PointClasses = `apexcharts-marker w${(Math.random() + 1)
|
||||
.toString(36)
|
||||
.substring(4)}`
|
||||
if (
|
||||
(w.config.chart.type === 'line' || w.config.chart.type === 'area') &&
|
||||
!w.globals.comboCharts &&
|
||||
!w.config.tooltip.intersect
|
||||
) {
|
||||
PointClasses += ' no-pointer-events'
|
||||
}
|
||||
|
||||
let elPointOptions = marker.getMarkerConfig({
|
||||
cssClass: PointClasses,
|
||||
seriesIndex: Number(pointsMain.getAttribute('data:realIndex')) // fixes apexcharts/apexcharts.js #1427
|
||||
})
|
||||
|
||||
point = graphics.drawMarker(0, 0, elPointOptions)
|
||||
|
||||
point.node.setAttribute('default-marker-size', 0)
|
||||
|
||||
let elPointsG = document.createElementNS(w.globals.SVGNS, 'g')
|
||||
elPointsG.classList.add('apexcharts-series-markers')
|
||||
|
||||
elPointsG.appendChild(point.node)
|
||||
pointsMain.appendChild(elPointsG)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enlargeCurrentPoint(rel, point, x = null, y = null) {
|
||||
let w = this.w
|
||||
|
||||
if (w.config.chart.type !== 'bubble') {
|
||||
this.newPointSize(rel, point)
|
||||
}
|
||||
|
||||
let cx = point.getAttribute('cx')
|
||||
let cy = point.getAttribute('cy')
|
||||
|
||||
if (x !== null && y !== null) {
|
||||
cx = x
|
||||
cy = y
|
||||
}
|
||||
|
||||
this.tooltipPosition.moveXCrosshairs(cx)
|
||||
|
||||
if (!this.fixedTooltip) {
|
||||
if (w.config.chart.type === 'radar') {
|
||||
const elGrid = this.ttCtx.getElGrid()
|
||||
const seriesBound = elGrid.getBoundingClientRect()
|
||||
|
||||
cx = this.ttCtx.e.clientX - seriesBound.left
|
||||
}
|
||||
|
||||
this.tooltipPosition.moveTooltip(cx, cy, w.config.markers.hover.size)
|
||||
}
|
||||
}
|
||||
|
||||
enlargePoints(j) {
|
||||
let w = this.w
|
||||
let me = this
|
||||
const ttCtx = this.ttCtx
|
||||
|
||||
let col = j
|
||||
|
||||
let points = w.globals.dom.baseEl.querySelectorAll(
|
||||
'.apexcharts-series:not(.apexcharts-series-collapsed) .apexcharts-marker'
|
||||
)
|
||||
|
||||
let newSize = w.config.markers.hover.size
|
||||
|
||||
for (let p = 0; p < points.length; p++) {
|
||||
let rel = points[p].getAttribute('rel')
|
||||
let index = points[p].getAttribute('index')
|
||||
|
||||
if (newSize === undefined) {
|
||||
newSize =
|
||||
w.globals.markers.size[index] + w.config.markers.hover.sizeOffset
|
||||
}
|
||||
|
||||
if (col === parseInt(rel, 10)) {
|
||||
me.newPointSize(col, points[p])
|
||||
|
||||
let cx = points[p].getAttribute('cx')
|
||||
let cy = points[p].getAttribute('cy')
|
||||
|
||||
me.tooltipPosition.moveXCrosshairs(cx)
|
||||
|
||||
if (!ttCtx.fixedTooltip) {
|
||||
me.tooltipPosition.moveTooltip(cx, cy, newSize)
|
||||
}
|
||||
} else {
|
||||
me.oldPointSize(points[p])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newPointSize(rel, point) {
|
||||
let w = this.w
|
||||
let newSize = w.config.markers.hover.size
|
||||
|
||||
let elPoint =
|
||||
rel === 0 ? point.parentNode.firstChild : point.parentNode.lastChild
|
||||
|
||||
if (elPoint.getAttribute('default-marker-size') !== '0') {
|
||||
const index = parseInt(elPoint.getAttribute('index'), 10)
|
||||
if (newSize === undefined) {
|
||||
newSize =
|
||||
w.globals.markers.size[index] + w.config.markers.hover.sizeOffset
|
||||
}
|
||||
|
||||
if (newSize < 0) newSize = 0
|
||||
elPoint.setAttribute('r', newSize)
|
||||
}
|
||||
}
|
||||
|
||||
oldPointSize(point) {
|
||||
const size = parseFloat(point.getAttribute('default-marker-size'))
|
||||
point.setAttribute('r', size)
|
||||
}
|
||||
|
||||
resetPointsSize() {
|
||||
let w = this.w
|
||||
|
||||
let points = w.globals.dom.baseEl.querySelectorAll(
|
||||
'.apexcharts-series:not(.apexcharts-series-collapsed) .apexcharts-marker'
|
||||
)
|
||||
|
||||
for (let p = 0; p < points.length; p++) {
|
||||
const size = parseFloat(points[p].getAttribute('default-marker-size'))
|
||||
if (Utils.isNumber(size) && size >= 0) {
|
||||
points[p].setAttribute('r', size)
|
||||
} else {
|
||||
points[p].setAttribute('r', 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+442
@@ -0,0 +1,442 @@
|
||||
import Graphics from '../Graphics'
|
||||
import Series from '../Series'
|
||||
|
||||
/**
|
||||
* ApexCharts Tooltip.Position Class to move the tooltip based on x and y position.
|
||||
*
|
||||
* @module Tooltip.Position
|
||||
**/
|
||||
|
||||
export default class Position {
|
||||
constructor(tooltipContext) {
|
||||
this.ttCtx = tooltipContext
|
||||
this.ctx = tooltipContext.ctx
|
||||
this.w = tooltipContext.w
|
||||
}
|
||||
|
||||
/**
|
||||
* This will move the crosshair (the vertical/horz line that moves along with mouse)
|
||||
* Along with this, this function also calls the xaxisMove function
|
||||
* @memberof Position
|
||||
* @param {int} - cx = point's x position, wherever point's x is, you need to move crosshair
|
||||
*/
|
||||
moveXCrosshairs(cx, j = null) {
|
||||
const ttCtx = this.ttCtx
|
||||
let w = this.w
|
||||
|
||||
const xcrosshairs = ttCtx.getElXCrosshairs()
|
||||
|
||||
let x = cx - ttCtx.xcrosshairsWidth / 2
|
||||
|
||||
let tickAmount = w.globals.labels.slice().length
|
||||
if (j !== null) {
|
||||
x = (w.globals.gridWidth / tickAmount) * j
|
||||
}
|
||||
|
||||
if (xcrosshairs !== null && !w.globals.isBarHorizontal) {
|
||||
xcrosshairs.setAttribute('x', x)
|
||||
xcrosshairs.setAttribute('x1', x)
|
||||
xcrosshairs.setAttribute('x2', x)
|
||||
xcrosshairs.setAttribute('y2', w.globals.gridHeight)
|
||||
xcrosshairs.classList.add('apexcharts-active')
|
||||
}
|
||||
|
||||
if (x < 0) {
|
||||
x = 0
|
||||
}
|
||||
|
||||
if (x > w.globals.gridWidth) {
|
||||
x = w.globals.gridWidth
|
||||
}
|
||||
|
||||
if (ttCtx.isXAxisTooltipEnabled) {
|
||||
let tx = x
|
||||
if (
|
||||
w.config.xaxis.crosshairs.width === 'tickWidth' ||
|
||||
w.config.xaxis.crosshairs.width === 'barWidth'
|
||||
) {
|
||||
tx = x + ttCtx.xcrosshairsWidth / 2
|
||||
}
|
||||
this.moveXAxisTooltip(tx)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This will move the crosshair (the vertical/horz line that moves along with mouse)
|
||||
* Along with this, this function also calls the xaxisMove function
|
||||
* @memberof Position
|
||||
* @param {int} - cx = point's x position, wherever point's x is, you need to move crosshair
|
||||
*/
|
||||
moveYCrosshairs(cy) {
|
||||
const ttCtx = this.ttCtx
|
||||
|
||||
if (ttCtx.ycrosshairs !== null) {
|
||||
Graphics.setAttrs(ttCtx.ycrosshairs, {
|
||||
y1: cy,
|
||||
y2: cy,
|
||||
})
|
||||
}
|
||||
if (ttCtx.ycrosshairsHidden !== null) {
|
||||
Graphics.setAttrs(ttCtx.ycrosshairsHidden, {
|
||||
y1: cy,
|
||||
y2: cy,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
** AxisTooltip is the small rectangle which appears on x axis with x value, when user moves
|
||||
* @memberof Position
|
||||
* @param {int} - cx = point's x position, wherever point's x is, you need to move
|
||||
*/
|
||||
moveXAxisTooltip(cx) {
|
||||
let w = this.w
|
||||
const ttCtx = this.ttCtx
|
||||
|
||||
if (ttCtx.xaxisTooltip !== null && ttCtx.xcrosshairsWidth !== 0) {
|
||||
ttCtx.xaxisTooltip.classList.add('apexcharts-active')
|
||||
|
||||
let cy =
|
||||
ttCtx.xaxisOffY +
|
||||
w.config.xaxis.tooltip.offsetY +
|
||||
w.globals.translateY +
|
||||
1 +
|
||||
w.config.xaxis.offsetY
|
||||
|
||||
let xaxisTTText = ttCtx.xaxisTooltip.getBoundingClientRect()
|
||||
let xaxisTTTextWidth = xaxisTTText.width
|
||||
|
||||
cx = cx - xaxisTTTextWidth / 2
|
||||
|
||||
if (!isNaN(cx)) {
|
||||
cx = cx + w.globals.translateX
|
||||
|
||||
let textRect = 0
|
||||
const graphics = new Graphics(this.ctx)
|
||||
textRect = graphics.getTextRects(ttCtx.xaxisTooltipText.innerHTML)
|
||||
|
||||
ttCtx.xaxisTooltipText.style.minWidth = textRect.width + 'px'
|
||||
ttCtx.xaxisTooltip.style.left = cx + 'px'
|
||||
ttCtx.xaxisTooltip.style.top = cy + 'px'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
moveYAxisTooltip(index) {
|
||||
const w = this.w
|
||||
const ttCtx = this.ttCtx
|
||||
|
||||
if (ttCtx.yaxisTTEls === null) {
|
||||
ttCtx.yaxisTTEls = w.globals.dom.baseEl.querySelectorAll(
|
||||
'.apexcharts-yaxistooltip'
|
||||
)
|
||||
}
|
||||
|
||||
const ycrosshairsHiddenRectY1 = parseInt(
|
||||
ttCtx.ycrosshairsHidden.getAttribute('y1'),
|
||||
10
|
||||
)
|
||||
let cy = w.globals.translateY + ycrosshairsHiddenRectY1
|
||||
|
||||
const yAxisTTRect = ttCtx.yaxisTTEls[index].getBoundingClientRect()
|
||||
const yAxisTTHeight = yAxisTTRect.height
|
||||
let cx = w.globals.translateYAxisX[index] - 2
|
||||
|
||||
if (w.config.yaxis[index].opposite) {
|
||||
cx = cx - 26
|
||||
}
|
||||
|
||||
cy = cy - yAxisTTHeight / 2
|
||||
|
||||
if (w.globals.ignoreYAxisIndexes.indexOf(index) === -1) {
|
||||
ttCtx.yaxisTTEls[index].classList.add('apexcharts-active')
|
||||
ttCtx.yaxisTTEls[index].style.top = cy + 'px'
|
||||
ttCtx.yaxisTTEls[index].style.left =
|
||||
cx + w.config.yaxis[index].tooltip.offsetX + 'px'
|
||||
} else {
|
||||
ttCtx.yaxisTTEls[index].classList.remove('apexcharts-active')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
** moves the whole tooltip by changing x, y attrs
|
||||
* @memberof Position
|
||||
* @param {int} - cx = point's x position, wherever point's x is, you need to move tooltip
|
||||
* @param {int} - cy = point's y position, wherever point's y is, you need to move tooltip
|
||||
* @param {int} - r = point's radius
|
||||
*/
|
||||
moveTooltip(cx, cy, r = null) {
|
||||
let w = this.w
|
||||
|
||||
let ttCtx = this.ttCtx
|
||||
const tooltipEl = ttCtx.getElTooltip()
|
||||
let tooltipRect = ttCtx.tooltipRect
|
||||
|
||||
let pointR = r !== null ? parseFloat(r) : 1
|
||||
|
||||
let x = parseFloat(cx) + pointR + 5
|
||||
let y = parseFloat(cy) + pointR / 2 // - tooltipRect.ttHeight / 2
|
||||
|
||||
if (x > w.globals.gridWidth / 2) {
|
||||
x = x - tooltipRect.ttWidth - pointR - 10
|
||||
}
|
||||
|
||||
if (x > w.globals.gridWidth - tooltipRect.ttWidth - 10) {
|
||||
x = w.globals.gridWidth - tooltipRect.ttWidth
|
||||
}
|
||||
|
||||
if (x < -20) {
|
||||
x = -20
|
||||
}
|
||||
|
||||
if (w.config.tooltip.followCursor) {
|
||||
const elGrid = ttCtx.getElGrid()
|
||||
const seriesBound = elGrid.getBoundingClientRect()
|
||||
|
||||
x = ttCtx.e.clientX - seriesBound.left
|
||||
if (x > w.globals.gridWidth / 2) {
|
||||
x = x - ttCtx.tooltipRect.ttWidth
|
||||
}
|
||||
y = ttCtx.e.clientY + w.globals.translateY - seriesBound.top
|
||||
if (y > w.globals.gridHeight / 2) {
|
||||
y = y - ttCtx.tooltipRect.ttHeight
|
||||
}
|
||||
} else {
|
||||
if (!w.globals.isBarHorizontal) {
|
||||
if (tooltipRect.ttHeight / 2 + y > w.globals.gridHeight) {
|
||||
y = w.globals.gridHeight - tooltipRect.ttHeight + w.globals.translateY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isNaN(x)) {
|
||||
x = x + w.globals.translateX
|
||||
|
||||
tooltipEl.style.left = x + 'px'
|
||||
tooltipEl.style.top = y + 'px'
|
||||
}
|
||||
}
|
||||
|
||||
moveMarkers(i, j) {
|
||||
let w = this.w
|
||||
let ttCtx = this.ttCtx
|
||||
|
||||
if (w.globals.markers.size[i] > 0) {
|
||||
let allPoints = w.globals.dom.baseEl.querySelectorAll(
|
||||
` .apexcharts-series[data\\:realIndex='${i}'] .apexcharts-marker`
|
||||
)
|
||||
for (let p = 0; p < allPoints.length; p++) {
|
||||
if (parseInt(allPoints[p].getAttribute('rel'), 10) === j) {
|
||||
ttCtx.marker.resetPointsSize()
|
||||
ttCtx.marker.enlargeCurrentPoint(j, allPoints[p])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ttCtx.marker.resetPointsSize()
|
||||
this.moveDynamicPointOnHover(j, i)
|
||||
}
|
||||
}
|
||||
|
||||
// This function is used when you need to show markers/points only on hover -
|
||||
// DIFFERENT X VALUES in multiple series
|
||||
moveDynamicPointOnHover(j, capturedSeries) {
|
||||
let w = this.w
|
||||
let ttCtx = this.ttCtx
|
||||
let cx = 0
|
||||
let cy = 0
|
||||
|
||||
let pointsArr = w.globals.pointsArray
|
||||
|
||||
let hoverSize = ttCtx.tooltipUtil.getHoverMarkerSize(capturedSeries)
|
||||
|
||||
const serType = w.config.series[capturedSeries].type
|
||||
if (
|
||||
serType &&
|
||||
(serType === 'column' ||
|
||||
serType === 'candlestick' ||
|
||||
serType === 'boxPlot')
|
||||
) {
|
||||
// fix error mentioned in #811
|
||||
return
|
||||
}
|
||||
|
||||
cx = pointsArr[capturedSeries][j][0]
|
||||
cy = pointsArr[capturedSeries][j][1] ? pointsArr[capturedSeries][j][1] : 0
|
||||
|
||||
let point = w.globals.dom.baseEl.querySelector(
|
||||
`.apexcharts-series[data\\:realIndex='${capturedSeries}'] .apexcharts-series-markers circle`
|
||||
)
|
||||
|
||||
if (point && cy < w.globals.gridHeight && cy > 0) {
|
||||
point.setAttribute('r', hoverSize)
|
||||
|
||||
point.setAttribute('cx', cx)
|
||||
point.setAttribute('cy', cy)
|
||||
}
|
||||
|
||||
// point.style.opacity = w.config.markers.hover.opacity
|
||||
|
||||
this.moveXCrosshairs(cx)
|
||||
|
||||
if (!ttCtx.fixedTooltip) {
|
||||
this.moveTooltip(cx, cy, hoverSize)
|
||||
}
|
||||
}
|
||||
|
||||
// This function is used when you need to show markers/points only on hover -
|
||||
// SAME X VALUES in multiple series
|
||||
moveDynamicPointsOnHover(j) {
|
||||
const ttCtx = this.ttCtx
|
||||
let w = ttCtx.w
|
||||
let cx = 0
|
||||
let cy = 0
|
||||
let activeSeries = 0
|
||||
|
||||
let pointsArr = w.globals.pointsArray
|
||||
|
||||
let series = new Series(this.ctx)
|
||||
activeSeries = series.getActiveConfigSeriesIndex('asc', [
|
||||
'line',
|
||||
'area',
|
||||
'scatter',
|
||||
'bubble',
|
||||
])
|
||||
|
||||
let hoverSize = ttCtx.tooltipUtil.getHoverMarkerSize(activeSeries)
|
||||
|
||||
if (pointsArr[activeSeries]) {
|
||||
cx = pointsArr[activeSeries][j][0]
|
||||
cy = pointsArr[activeSeries][j][1]
|
||||
}
|
||||
|
||||
let points = ttCtx.tooltipUtil.getAllMarkers()
|
||||
|
||||
if (points !== null) {
|
||||
for (let p = 0; p < w.globals.series.length; p++) {
|
||||
let pointArr = pointsArr[p]
|
||||
|
||||
if (w.globals.comboCharts) {
|
||||
// in a combo chart, if column charts are present, markers will not match with the number of series, hence this patch to push a null value in points array
|
||||
if (typeof pointArr === 'undefined') {
|
||||
// nodelist to array
|
||||
points.splice(p, 0, null)
|
||||
}
|
||||
}
|
||||
if (pointArr && pointArr.length) {
|
||||
let pcy = pointsArr[p][j][1]
|
||||
let pcy2
|
||||
points[p].setAttribute('cx', cx)
|
||||
|
||||
if (w.config.chart.type === 'rangeArea' && !w.globals.comboCharts) {
|
||||
const rangeStartIndex = j + w.globals.series[p].length
|
||||
pcy2 = pointsArr[p][rangeStartIndex][1]
|
||||
const pcyDiff = Math.abs(pcy - pcy2) / 2
|
||||
|
||||
pcy = pcy - pcyDiff
|
||||
}
|
||||
if (
|
||||
pcy !== null &&
|
||||
!isNaN(pcy) &&
|
||||
pcy < w.globals.gridHeight + hoverSize &&
|
||||
pcy + hoverSize > 0
|
||||
) {
|
||||
points[p] && points[p].setAttribute('r', hoverSize)
|
||||
points[p] && points[p].setAttribute('cy', pcy)
|
||||
} else {
|
||||
points[p] && points[p].setAttribute('r', 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.moveXCrosshairs(cx)
|
||||
|
||||
if (!ttCtx.fixedTooltip) {
|
||||
this.moveTooltip(cx, cy || w.globals.gridHeight, hoverSize)
|
||||
}
|
||||
}
|
||||
|
||||
moveStickyTooltipOverBars(j, capturedSeries) {
|
||||
const w = this.w
|
||||
const ttCtx = this.ttCtx
|
||||
|
||||
let barLen = w.globals.columnSeries
|
||||
? w.globals.columnSeries.length
|
||||
: w.globals.series.length
|
||||
|
||||
let i =
|
||||
barLen >= 2 && barLen % 2 === 0
|
||||
? Math.floor(barLen / 2)
|
||||
: Math.floor(barLen / 2) + 1
|
||||
|
||||
if (w.globals.isBarHorizontal) {
|
||||
let series = new Series(this.ctx)
|
||||
i = series.getActiveConfigSeriesIndex('desc') + 1
|
||||
}
|
||||
let jBar = w.globals.dom.baseEl.querySelector(
|
||||
`.apexcharts-bar-series .apexcharts-series[rel='${i}'] path[j='${j}'], .apexcharts-candlestick-series .apexcharts-series[rel='${i}'] path[j='${j}'], .apexcharts-boxPlot-series .apexcharts-series[rel='${i}'] path[j='${j}'], .apexcharts-rangebar-series .apexcharts-series[rel='${i}'] path[j='${j}']`
|
||||
)
|
||||
if (!jBar && typeof capturedSeries === 'number') {
|
||||
// Try with captured series index
|
||||
jBar = w.globals.dom.baseEl.querySelector(
|
||||
`.apexcharts-bar-series .apexcharts-series[data\\:realIndex='${capturedSeries}'] path[j='${j}'],
|
||||
.apexcharts-candlestick-series .apexcharts-series[data\\:realIndex='${capturedSeries}'] path[j='${j}'],
|
||||
.apexcharts-boxPlot-series .apexcharts-series[data\\:realIndex='${capturedSeries}'] path[j='${j}'],
|
||||
.apexcharts-rangebar-series .apexcharts-series[data\\:realIndex='${capturedSeries}'] path[j='${j}']`
|
||||
)
|
||||
}
|
||||
|
||||
let bcx = jBar ? parseFloat(jBar.getAttribute('cx')) : 0
|
||||
let bcy = jBar ? parseFloat(jBar.getAttribute('cy')) : 0
|
||||
let bw = jBar ? parseFloat(jBar.getAttribute('barWidth')) : 0
|
||||
|
||||
const elGrid = ttCtx.getElGrid()
|
||||
let seriesBound = elGrid.getBoundingClientRect()
|
||||
|
||||
const isBoxOrCandle =
|
||||
jBar &&
|
||||
(jBar.classList.contains('apexcharts-candlestick-area') ||
|
||||
jBar.classList.contains('apexcharts-boxPlot-area'))
|
||||
if (w.globals.isXNumeric) {
|
||||
if (jBar && !isBoxOrCandle) {
|
||||
bcx = bcx - (barLen % 2 !== 0 ? bw / 2 : 0)
|
||||
}
|
||||
|
||||
if (
|
||||
jBar && // fixes apexcharts.js#2354
|
||||
isBoxOrCandle &&
|
||||
w.globals.comboCharts
|
||||
) {
|
||||
bcx = bcx - bw / 2
|
||||
}
|
||||
} else {
|
||||
if (!w.globals.isBarHorizontal) {
|
||||
bcx =
|
||||
ttCtx.xAxisTicksPositions[j - 1] + ttCtx.dataPointsDividedWidth / 2
|
||||
if (isNaN(bcx)) {
|
||||
bcx = ttCtx.xAxisTicksPositions[j] - ttCtx.dataPointsDividedWidth / 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!w.globals.isBarHorizontal) {
|
||||
if (w.config.tooltip.followCursor) {
|
||||
bcy = ttCtx.e.clientY - seriesBound.top - ttCtx.tooltipRect.ttHeight / 2
|
||||
} else {
|
||||
if (bcy + ttCtx.tooltipRect.ttHeight + 15 > w.globals.gridHeight) {
|
||||
bcy = w.globals.gridHeight
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bcy = bcy - ttCtx.tooltipRect.ttHeight
|
||||
}
|
||||
|
||||
if (!w.globals.isBarHorizontal) {
|
||||
this.moveXCrosshairs(bcx)
|
||||
}
|
||||
|
||||
if (!ttCtx.fixedTooltip) {
|
||||
this.moveTooltip(bcx, bcy || w.globals.gridHeight)
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
### AxesTooltip.js
|
||||
This file deals with the x-axis and y-axis tooltips.
|
||||
|
||||
### Intersect.js
|
||||
This file deals with functions related to intersecting tooltips (tooltips that appear when user hovers directly over a data-point whether).
|
||||
|
||||
### Labels.js
|
||||
This file deals with printing actual text on the tooltip.
|
||||
|
||||
### Marker.js
|
||||
This file deals with the markers that appear near tooltip in line/area charts. These markers helps the user to associate the data-points and the values that are shown in the tooltip
|
||||
|
||||
### Position.js
|
||||
This file deals with positioning of the tooltip.
|
||||
|
||||
### Tooltip.js
|
||||
This is the primary file which is an entry point for all tooltip related functionality.
|
||||
|
||||
### Utils.js
|
||||
Helper functions related to tooltips.
|
||||
+900
@@ -0,0 +1,900 @@
|
||||
import Labels from './Labels'
|
||||
import Position from './Position'
|
||||
import Marker from './Marker'
|
||||
import Intersect from './Intersect'
|
||||
import AxesTooltip from './AxesTooltip'
|
||||
import Graphics from '../Graphics'
|
||||
import Series from '../Series'
|
||||
import XAxis from './../axes/XAxis'
|
||||
import Utils from './Utils'
|
||||
|
||||
/**
|
||||
* ApexCharts Core Tooltip Class to handle the tooltip generation.
|
||||
*
|
||||
* @module Tooltip
|
||||
**/
|
||||
|
||||
export default class Tooltip {
|
||||
constructor(ctx) {
|
||||
this.ctx = ctx
|
||||
this.w = ctx.w
|
||||
const w = this.w
|
||||
|
||||
this.tConfig = w.config.tooltip
|
||||
|
||||
this.tooltipUtil = new Utils(this)
|
||||
this.tooltipLabels = new Labels(this)
|
||||
this.tooltipPosition = new Position(this)
|
||||
this.marker = new Marker(this)
|
||||
this.intersect = new Intersect(this)
|
||||
this.axesTooltip = new AxesTooltip(this)
|
||||
this.showOnIntersect = this.tConfig.intersect
|
||||
this.showTooltipTitle = this.tConfig.x.show
|
||||
this.fixedTooltip = this.tConfig.fixed.enabled
|
||||
this.xaxisTooltip = null
|
||||
this.yaxisTTEls = null
|
||||
this.isBarShared = !w.globals.isBarHorizontal && this.tConfig.shared
|
||||
this.lastHoverTime = Date.now()
|
||||
}
|
||||
|
||||
getElTooltip(ctx) {
|
||||
if (!ctx) ctx = this
|
||||
if (!ctx.w.globals.dom.baseEl) return null
|
||||
|
||||
return ctx.w.globals.dom.baseEl.querySelector('.apexcharts-tooltip')
|
||||
}
|
||||
|
||||
getElXCrosshairs() {
|
||||
return this.w.globals.dom.baseEl.querySelector('.apexcharts-xcrosshairs')
|
||||
}
|
||||
|
||||
getElGrid() {
|
||||
return this.w.globals.dom.baseEl.querySelector('.apexcharts-grid')
|
||||
}
|
||||
|
||||
drawTooltip(xyRatios) {
|
||||
let w = this.w
|
||||
this.xyRatios = xyRatios
|
||||
this.isXAxisTooltipEnabled =
|
||||
w.config.xaxis.tooltip.enabled && w.globals.axisCharts
|
||||
this.yaxisTooltips = w.config.yaxis.map((y, i) => {
|
||||
return y.show && y.tooltip.enabled && w.globals.axisCharts ? true : false
|
||||
})
|
||||
this.allTooltipSeriesGroups = []
|
||||
|
||||
if (!w.globals.axisCharts) {
|
||||
this.showTooltipTitle = false
|
||||
}
|
||||
|
||||
const tooltipEl = document.createElement('div')
|
||||
tooltipEl.classList.add('apexcharts-tooltip')
|
||||
if (w.config.tooltip.cssClass) {
|
||||
tooltipEl.classList.add(w.config.tooltip.cssClass)
|
||||
}
|
||||
tooltipEl.classList.add(`apexcharts-theme-${this.tConfig.theme}`)
|
||||
w.globals.dom.elWrap.appendChild(tooltipEl)
|
||||
|
||||
if (w.globals.axisCharts) {
|
||||
this.axesTooltip.drawXaxisTooltip()
|
||||
this.axesTooltip.drawYaxisTooltip()
|
||||
this.axesTooltip.setXCrosshairWidth()
|
||||
this.axesTooltip.handleYCrosshair()
|
||||
|
||||
let xAxis = new XAxis(this.ctx)
|
||||
this.xAxisTicksPositions = xAxis.getXAxisTicksPositions()
|
||||
}
|
||||
|
||||
// we forcefully set intersect true for these conditions
|
||||
if (
|
||||
(w.globals.comboCharts ||
|
||||
this.tConfig.intersect ||
|
||||
w.config.chart.type === 'rangeBar') &&
|
||||
!this.tConfig.shared
|
||||
) {
|
||||
this.showOnIntersect = true
|
||||
}
|
||||
|
||||
if (w.config.markers.size === 0 || w.globals.markers.largestSize === 0) {
|
||||
// when user don't want to show points all the time, but only on when hovering on series
|
||||
this.marker.drawDynamicPoints(this)
|
||||
}
|
||||
|
||||
// no visible series, exit
|
||||
if (w.globals.collapsedSeries.length === w.globals.series.length) return
|
||||
|
||||
this.dataPointsDividedHeight = w.globals.gridHeight / w.globals.dataPoints
|
||||
this.dataPointsDividedWidth = w.globals.gridWidth / w.globals.dataPoints
|
||||
|
||||
if (this.showTooltipTitle) {
|
||||
this.tooltipTitle = document.createElement('div')
|
||||
this.tooltipTitle.classList.add('apexcharts-tooltip-title')
|
||||
this.tooltipTitle.style.fontFamily =
|
||||
this.tConfig.style.fontFamily || w.config.chart.fontFamily
|
||||
this.tooltipTitle.style.fontSize = this.tConfig.style.fontSize
|
||||
tooltipEl.appendChild(this.tooltipTitle)
|
||||
}
|
||||
|
||||
let ttItemsCnt = w.globals.series.length // whether shared or not, default is shared
|
||||
if ((w.globals.xyCharts || w.globals.comboCharts) && this.tConfig.shared) {
|
||||
if (!this.showOnIntersect) {
|
||||
ttItemsCnt = w.globals.series.length
|
||||
} else {
|
||||
ttItemsCnt = 1
|
||||
}
|
||||
}
|
||||
|
||||
this.legendLabels = w.globals.dom.baseEl.querySelectorAll(
|
||||
'.apexcharts-legend-text'
|
||||
)
|
||||
|
||||
this.ttItems = this.createTTElements(ttItemsCnt)
|
||||
this.addSVGEvents()
|
||||
}
|
||||
|
||||
createTTElements(ttItemsCnt) {
|
||||
const w = this.w
|
||||
let ttItems = []
|
||||
|
||||
const tooltipEl = this.getElTooltip()
|
||||
for (let i = 0; i < ttItemsCnt; i++) {
|
||||
let gTxt = document.createElement('div')
|
||||
gTxt.classList.add('apexcharts-tooltip-series-group')
|
||||
gTxt.style.order = w.config.tooltip.inverseOrder ? ttItemsCnt - i : i + 1
|
||||
if (
|
||||
this.tConfig.shared &&
|
||||
this.tConfig.enabledOnSeries &&
|
||||
Array.isArray(this.tConfig.enabledOnSeries)
|
||||
) {
|
||||
if (this.tConfig.enabledOnSeries.indexOf(i) < 0) {
|
||||
gTxt.classList.add('apexcharts-tooltip-series-group-hidden')
|
||||
}
|
||||
}
|
||||
|
||||
let point = document.createElement('span')
|
||||
point.classList.add('apexcharts-tooltip-marker')
|
||||
point.style.backgroundColor = w.globals.colors[i]
|
||||
gTxt.appendChild(point)
|
||||
|
||||
const gYZ = document.createElement('div')
|
||||
gYZ.classList.add('apexcharts-tooltip-text')
|
||||
|
||||
gYZ.style.fontFamily =
|
||||
this.tConfig.style.fontFamily || w.config.chart.fontFamily
|
||||
gYZ.style.fontSize = this.tConfig.style.fontSize
|
||||
;['y', 'goals', 'z'].forEach((g) => {
|
||||
const gValText = document.createElement('div')
|
||||
gValText.classList.add(`apexcharts-tooltip-${g}-group`)
|
||||
|
||||
let txtLabel = document.createElement('span')
|
||||
txtLabel.classList.add(`apexcharts-tooltip-text-${g}-label`)
|
||||
gValText.appendChild(txtLabel)
|
||||
|
||||
let txtValue = document.createElement('span')
|
||||
txtValue.classList.add(`apexcharts-tooltip-text-${g}-value`)
|
||||
gValText.appendChild(txtValue)
|
||||
|
||||
gYZ.appendChild(gValText)
|
||||
})
|
||||
|
||||
gTxt.appendChild(gYZ)
|
||||
|
||||
tooltipEl.appendChild(gTxt)
|
||||
|
||||
ttItems.push(gTxt)
|
||||
}
|
||||
|
||||
return ttItems
|
||||
}
|
||||
|
||||
addSVGEvents() {
|
||||
const w = this.w
|
||||
let type = w.config.chart.type
|
||||
const tooltipEl = this.getElTooltip()
|
||||
|
||||
const commonBar = !!(
|
||||
type === 'bar' ||
|
||||
type === 'candlestick' ||
|
||||
type === 'boxPlot' ||
|
||||
type === 'rangeBar'
|
||||
)
|
||||
|
||||
const chartWithmarkers =
|
||||
type === 'area' ||
|
||||
type === 'line' ||
|
||||
type === 'scatter' ||
|
||||
type === 'bubble' ||
|
||||
type === 'radar'
|
||||
|
||||
let hoverArea = w.globals.dom.Paper.node
|
||||
|
||||
const elGrid = this.getElGrid()
|
||||
if (elGrid) {
|
||||
this.seriesBound = elGrid.getBoundingClientRect()
|
||||
}
|
||||
|
||||
let tooltipY = []
|
||||
let tooltipX = []
|
||||
|
||||
let seriesHoverParams = {
|
||||
hoverArea,
|
||||
elGrid,
|
||||
tooltipEl,
|
||||
tooltipY,
|
||||
tooltipX,
|
||||
ttItems: this.ttItems,
|
||||
}
|
||||
|
||||
let points
|
||||
|
||||
if (w.globals.axisCharts) {
|
||||
if (chartWithmarkers) {
|
||||
points = w.globals.dom.baseEl.querySelectorAll(
|
||||
".apexcharts-series[data\\:longestSeries='true'] .apexcharts-marker"
|
||||
)
|
||||
} else if (commonBar) {
|
||||
points = w.globals.dom.baseEl.querySelectorAll(
|
||||
'.apexcharts-series .apexcharts-bar-area, .apexcharts-series .apexcharts-candlestick-area, .apexcharts-series .apexcharts-boxPlot-area, .apexcharts-series .apexcharts-rangebar-area'
|
||||
)
|
||||
} else if (type === 'heatmap' || type === 'treemap') {
|
||||
points = w.globals.dom.baseEl.querySelectorAll(
|
||||
'.apexcharts-series .apexcharts-heatmap, .apexcharts-series .apexcharts-treemap'
|
||||
)
|
||||
}
|
||||
|
||||
if (points && points.length) {
|
||||
for (let p = 0; p < points.length; p++) {
|
||||
tooltipY.push(points[p].getAttribute('cy'))
|
||||
tooltipX.push(points[p].getAttribute('cx'))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const validSharedChartTypes =
|
||||
(w.globals.xyCharts && !this.showOnIntersect) ||
|
||||
(w.globals.comboCharts && !this.showOnIntersect) ||
|
||||
(commonBar && this.tooltipUtil.hasBars() && this.tConfig.shared)
|
||||
|
||||
if (validSharedChartTypes) {
|
||||
this.addPathsEventListeners([hoverArea], seriesHoverParams)
|
||||
} else if (
|
||||
(commonBar && !w.globals.comboCharts) ||
|
||||
(chartWithmarkers && this.showOnIntersect)
|
||||
) {
|
||||
this.addDatapointEventsListeners(seriesHoverParams)
|
||||
} else if (
|
||||
!w.globals.axisCharts ||
|
||||
type === 'heatmap' ||
|
||||
type === 'treemap'
|
||||
) {
|
||||
let seriesAll =
|
||||
w.globals.dom.baseEl.querySelectorAll('.apexcharts-series')
|
||||
this.addPathsEventListeners(seriesAll, seriesHoverParams)
|
||||
}
|
||||
|
||||
if (this.showOnIntersect) {
|
||||
let lineAreaPoints = w.globals.dom.baseEl.querySelectorAll(
|
||||
'.apexcharts-line-series .apexcharts-marker, .apexcharts-area-series .apexcharts-marker'
|
||||
)
|
||||
if (lineAreaPoints.length > 0) {
|
||||
// if we find any lineSeries, addEventListeners for them
|
||||
this.addPathsEventListeners(lineAreaPoints, seriesHoverParams)
|
||||
}
|
||||
|
||||
// combo charts may have bars, so add event listeners here too
|
||||
if (this.tooltipUtil.hasBars() && !this.tConfig.shared) {
|
||||
this.addDatapointEventsListeners(seriesHoverParams)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drawFixedTooltipRect() {
|
||||
let w = this.w
|
||||
|
||||
const tooltipEl = this.getElTooltip()
|
||||
|
||||
let tooltipRect = tooltipEl.getBoundingClientRect()
|
||||
|
||||
let ttWidth = tooltipRect.width + 10
|
||||
let ttHeight = tooltipRect.height + 10
|
||||
let x = this.tConfig.fixed.offsetX
|
||||
let y = this.tConfig.fixed.offsetY
|
||||
|
||||
const fixed = this.tConfig.fixed.position.toLowerCase()
|
||||
|
||||
if (fixed.indexOf('right') > -1) {
|
||||
x = x + w.globals.svgWidth - ttWidth + 10
|
||||
}
|
||||
if (fixed.indexOf('bottom') > -1) {
|
||||
y = y + w.globals.svgHeight - ttHeight - 10
|
||||
}
|
||||
|
||||
tooltipEl.style.left = x + 'px'
|
||||
tooltipEl.style.top = y + 'px'
|
||||
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
ttWidth,
|
||||
ttHeight,
|
||||
}
|
||||
}
|
||||
|
||||
addDatapointEventsListeners(seriesHoverParams) {
|
||||
let w = this.w
|
||||
let points = w.globals.dom.baseEl.querySelectorAll(
|
||||
'.apexcharts-series-markers .apexcharts-marker, .apexcharts-bar-area, .apexcharts-candlestick-area, .apexcharts-boxPlot-area, .apexcharts-rangebar-area'
|
||||
)
|
||||
this.addPathsEventListeners(points, seriesHoverParams)
|
||||
}
|
||||
|
||||
addPathsEventListeners(paths, opts) {
|
||||
let self = this
|
||||
|
||||
for (let p = 0; p < paths.length; p++) {
|
||||
let extendedOpts = {
|
||||
paths: paths[p],
|
||||
tooltipEl: opts.tooltipEl,
|
||||
tooltipY: opts.tooltipY,
|
||||
tooltipX: opts.tooltipX,
|
||||
elGrid: opts.elGrid,
|
||||
hoverArea: opts.hoverArea,
|
||||
ttItems: opts.ttItems,
|
||||
}
|
||||
|
||||
let events = ['mousemove', 'mouseup', 'touchmove', 'mouseout', 'touchend']
|
||||
|
||||
events.map((ev) => {
|
||||
return paths[p].addEventListener(
|
||||
ev,
|
||||
self.onSeriesHover.bind(self, extendedOpts),
|
||||
{ capture: false, passive: true }
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Check to see if the tooltips should be updated based on a mouse / touch event
|
||||
*/
|
||||
onSeriesHover(opt, e) {
|
||||
// If a user is moving their mouse quickly, don't bother updating the tooltip every single frame
|
||||
|
||||
const targetDelay = 100
|
||||
const timeSinceLastUpdate = Date.now() - this.lastHoverTime
|
||||
if (timeSinceLastUpdate >= targetDelay) {
|
||||
// The tooltip was last updated over 100ms ago - redraw it even if the user is still moving their
|
||||
// mouse so they get some feedback that their moves are being registered
|
||||
this.seriesHover(opt, e)
|
||||
} else {
|
||||
// The tooltip was last updated less than 100ms ago
|
||||
// Cancel any other delayed draw, so we don't show stale data
|
||||
clearTimeout(this.seriesHoverTimeout)
|
||||
|
||||
// Schedule the next draw so that it happens about 100ms after the last update
|
||||
this.seriesHoverTimeout = setTimeout(() => {
|
||||
this.seriesHover(opt, e)
|
||||
}, targetDelay - timeSinceLastUpdate)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** The actual series hover function
|
||||
*/
|
||||
seriesHover(opt, e) {
|
||||
this.lastHoverTime = Date.now()
|
||||
let chartGroups = []
|
||||
const w = this.w
|
||||
|
||||
// if user has more than one charts in group, we need to sync
|
||||
if (w.config.chart.group) {
|
||||
chartGroups = this.ctx.getGroupedCharts()
|
||||
}
|
||||
|
||||
if (
|
||||
w.globals.axisCharts &&
|
||||
((w.globals.minX === -Infinity && w.globals.maxX === Infinity) ||
|
||||
w.globals.dataPoints === 0)
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
if (chartGroups.length) {
|
||||
chartGroups.forEach((ch) => {
|
||||
const tooltipEl = this.getElTooltip(ch)
|
||||
|
||||
const newOpts = {
|
||||
paths: opt.paths,
|
||||
tooltipEl,
|
||||
tooltipY: opt.tooltipY,
|
||||
tooltipX: opt.tooltipX,
|
||||
elGrid: opt.elGrid,
|
||||
hoverArea: opt.hoverArea,
|
||||
ttItems: ch.w.globals.tooltip.ttItems,
|
||||
}
|
||||
|
||||
// all the charts should have the same minX and maxX (same xaxis) for multiple tooltips to work correctly
|
||||
if (
|
||||
ch.w.globals.minX === this.w.globals.minX &&
|
||||
ch.w.globals.maxX === this.w.globals.maxX
|
||||
) {
|
||||
ch.w.globals.tooltip.seriesHoverByContext({
|
||||
chartCtx: ch,
|
||||
ttCtx: ch.w.globals.tooltip,
|
||||
opt: newOpts,
|
||||
e,
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.seriesHoverByContext({
|
||||
chartCtx: this.ctx,
|
||||
ttCtx: this.w.globals.tooltip,
|
||||
opt,
|
||||
e,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
seriesHoverByContext({ chartCtx, ttCtx, opt, e }) {
|
||||
let w = chartCtx.w
|
||||
const tooltipEl = this.getElTooltip()
|
||||
|
||||
if (!tooltipEl) return
|
||||
|
||||
// tooltipRect is calculated on every mousemove, because the text is dynamic
|
||||
ttCtx.tooltipRect = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
ttWidth: tooltipEl.getBoundingClientRect().width,
|
||||
ttHeight: tooltipEl.getBoundingClientRect().height,
|
||||
}
|
||||
ttCtx.e = e
|
||||
|
||||
// highlight the current hovered bars
|
||||
if (
|
||||
ttCtx.tooltipUtil.hasBars() &&
|
||||
!w.globals.comboCharts &&
|
||||
!ttCtx.isBarShared
|
||||
) {
|
||||
if (this.tConfig.onDatasetHover.highlightDataSeries) {
|
||||
let series = new Series(chartCtx)
|
||||
series.toggleSeriesOnHover(e, e.target.parentNode)
|
||||
}
|
||||
}
|
||||
|
||||
if (ttCtx.fixedTooltip) {
|
||||
ttCtx.drawFixedTooltipRect()
|
||||
}
|
||||
|
||||
if (w.globals.axisCharts) {
|
||||
ttCtx.axisChartsTooltips({
|
||||
e,
|
||||
opt,
|
||||
tooltipRect: ttCtx.tooltipRect,
|
||||
})
|
||||
} else {
|
||||
// non-plot charts i.e pie/donut/circle
|
||||
ttCtx.nonAxisChartsTooltips({
|
||||
e,
|
||||
opt,
|
||||
tooltipRect: ttCtx.tooltipRect,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// tooltip handling for line/area/bar/columns/scatter
|
||||
axisChartsTooltips({ e, opt }) {
|
||||
let w = this.w
|
||||
let x, y
|
||||
|
||||
let seriesBound = opt.elGrid.getBoundingClientRect()
|
||||
|
||||
const clientX = e.type === 'touchmove' ? e.touches[0].clientX : e.clientX
|
||||
const clientY = e.type === 'touchmove' ? e.touches[0].clientY : e.clientY
|
||||
|
||||
this.clientY = clientY
|
||||
this.clientX = clientX
|
||||
|
||||
w.globals.capturedSeriesIndex = -1
|
||||
w.globals.capturedDataPointIndex = -1
|
||||
|
||||
if (
|
||||
clientY < seriesBound.top ||
|
||||
clientY > seriesBound.top + seriesBound.height
|
||||
) {
|
||||
this.handleMouseOut(opt)
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
Array.isArray(this.tConfig.enabledOnSeries) &&
|
||||
!w.config.tooltip.shared
|
||||
) {
|
||||
const index = parseInt(opt.paths.getAttribute('index'), 10)
|
||||
if (this.tConfig.enabledOnSeries.indexOf(index) < 0) {
|
||||
this.handleMouseOut(opt)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const tooltipEl = this.getElTooltip()
|
||||
const xcrosshairs = this.getElXCrosshairs()
|
||||
|
||||
let isStickyTooltip =
|
||||
w.globals.xyCharts ||
|
||||
(w.config.chart.type === 'bar' &&
|
||||
!w.globals.isBarHorizontal &&
|
||||
this.tooltipUtil.hasBars() &&
|
||||
this.tConfig.shared) ||
|
||||
(w.globals.comboCharts && this.tooltipUtil.hasBars())
|
||||
|
||||
if (
|
||||
e.type === 'mousemove' ||
|
||||
e.type === 'touchmove' ||
|
||||
e.type === 'mouseup'
|
||||
) {
|
||||
// there is no series to hover over
|
||||
if (
|
||||
w.globals.collapsedSeries.length +
|
||||
w.globals.ancillaryCollapsedSeries.length ===
|
||||
w.globals.series.length
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
if (xcrosshairs !== null) {
|
||||
xcrosshairs.classList.add('apexcharts-active')
|
||||
}
|
||||
|
||||
const hasYAxisTooltip = this.yaxisTooltips.filter((b) => {
|
||||
return b === true
|
||||
})
|
||||
if (this.ycrosshairs !== null && hasYAxisTooltip.length) {
|
||||
this.ycrosshairs.classList.add('apexcharts-active')
|
||||
}
|
||||
|
||||
if (isStickyTooltip && !this.showOnIntersect) {
|
||||
this.handleStickyTooltip(e, clientX, clientY, opt)
|
||||
} else {
|
||||
if (
|
||||
w.config.chart.type === 'heatmap' ||
|
||||
w.config.chart.type === 'treemap'
|
||||
) {
|
||||
let markerXY = this.intersect.handleHeatTreeTooltip({
|
||||
e,
|
||||
opt,
|
||||
x,
|
||||
y,
|
||||
type: w.config.chart.type,
|
||||
})
|
||||
x = markerXY.x
|
||||
y = markerXY.y
|
||||
|
||||
tooltipEl.style.left = x + 'px'
|
||||
tooltipEl.style.top = y + 'px'
|
||||
} else {
|
||||
if (this.tooltipUtil.hasBars()) {
|
||||
this.intersect.handleBarTooltip({
|
||||
e,
|
||||
opt,
|
||||
})
|
||||
}
|
||||
|
||||
if (this.tooltipUtil.hasMarkers()) {
|
||||
// intersect - line/area/scatter/bubble
|
||||
this.intersect.handleMarkerTooltip({
|
||||
e,
|
||||
opt,
|
||||
x,
|
||||
y,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.yaxisTooltips.length) {
|
||||
for (let yt = 0; yt < w.config.yaxis.length; yt++) {
|
||||
this.axesTooltip.drawYaxisTooltipText(yt, clientY, this.xyRatios)
|
||||
}
|
||||
}
|
||||
|
||||
opt.tooltipEl.classList.add('apexcharts-active')
|
||||
} else if (e.type === 'mouseout' || e.type === 'touchend') {
|
||||
this.handleMouseOut(opt)
|
||||
}
|
||||
}
|
||||
|
||||
// tooltip handling for pie/donuts
|
||||
nonAxisChartsTooltips({ e, opt, tooltipRect }) {
|
||||
let w = this.w
|
||||
let rel = opt.paths.getAttribute('rel')
|
||||
|
||||
const tooltipEl = this.getElTooltip()
|
||||
|
||||
let seriesBound = w.globals.dom.elWrap.getBoundingClientRect()
|
||||
|
||||
if (e.type === 'mousemove' || e.type === 'touchmove') {
|
||||
tooltipEl.classList.add('apexcharts-active')
|
||||
|
||||
this.tooltipLabels.drawSeriesTexts({
|
||||
ttItems: opt.ttItems,
|
||||
i: parseInt(rel, 10) - 1,
|
||||
shared: false,
|
||||
})
|
||||
|
||||
let x = w.globals.clientX - seriesBound.left - tooltipRect.ttWidth / 2
|
||||
let y = w.globals.clientY - seriesBound.top - tooltipRect.ttHeight - 10
|
||||
|
||||
tooltipEl.style.left = x + 'px'
|
||||
tooltipEl.style.top = y + 'px'
|
||||
|
||||
if (w.config.legend.tooltipHoverFormatter) {
|
||||
let legendFormatter = w.config.legend.tooltipHoverFormatter
|
||||
|
||||
const i = rel - 1
|
||||
const legendName =
|
||||
this.legendLabels[i].getAttribute('data:default-text')
|
||||
|
||||
let text = legendFormatter(legendName, {
|
||||
seriesIndex: i,
|
||||
dataPointIndex: i,
|
||||
w,
|
||||
})
|
||||
|
||||
this.legendLabels[i].innerHTML = text
|
||||
}
|
||||
} else if (e.type === 'mouseout' || e.type === 'touchend') {
|
||||
tooltipEl.classList.remove('apexcharts-active')
|
||||
if (w.config.legend.tooltipHoverFormatter) {
|
||||
this.legendLabels.forEach((l) => {
|
||||
const defaultText = l.getAttribute('data:default-text')
|
||||
l.innerHTML = decodeURIComponent(defaultText)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleStickyTooltip(e, clientX, clientY, opt) {
|
||||
const w = this.w
|
||||
let capj = this.tooltipUtil.getNearestValues({
|
||||
context: this,
|
||||
hoverArea: opt.hoverArea,
|
||||
elGrid: opt.elGrid,
|
||||
clientX,
|
||||
clientY,
|
||||
})
|
||||
|
||||
let j = capj.j
|
||||
let capturedSeries = capj.capturedSeries
|
||||
|
||||
if (w.globals.collapsedSeriesIndices.includes(capturedSeries))
|
||||
capturedSeries = null
|
||||
|
||||
const bounds = opt.elGrid.getBoundingClientRect()
|
||||
if (capj.hoverX < 0 || capj.hoverX > bounds.width) {
|
||||
this.handleMouseOut(opt)
|
||||
return
|
||||
}
|
||||
|
||||
if (capturedSeries !== null) {
|
||||
this.handleStickyCapturedSeries(e, capturedSeries, opt, j)
|
||||
} else {
|
||||
// couldn't capture any series. check if shared X is same,
|
||||
// if yes, draw a grouped tooltip
|
||||
if (this.tooltipUtil.isXoverlap(j) || w.globals.isBarHorizontal) {
|
||||
const firstVisibleSeries = w.globals.series.findIndex(
|
||||
(s, i) => !w.globals.collapsedSeriesIndices.includes(i)
|
||||
)
|
||||
this.create(e, this, firstVisibleSeries, j, opt.ttItems)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleStickyCapturedSeries(e, capturedSeries, opt, j) {
|
||||
const w = this.w
|
||||
if (!this.tConfig.shared) {
|
||||
let ignoreNull = w.globals.series[capturedSeries][j] === null
|
||||
if (ignoreNull) {
|
||||
this.handleMouseOut(opt)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof w.globals.series[capturedSeries][j] !== 'undefined') {
|
||||
if (
|
||||
this.tConfig.shared &&
|
||||
this.tooltipUtil.isXoverlap(j) &&
|
||||
this.tooltipUtil.isInitialSeriesSameLen()
|
||||
) {
|
||||
this.create(e, this, capturedSeries, j, opt.ttItems)
|
||||
} else {
|
||||
this.create(e, this, capturedSeries, j, opt.ttItems, false)
|
||||
}
|
||||
} else {
|
||||
if (this.tooltipUtil.isXoverlap(j)) {
|
||||
const firstVisibleSeries = w.globals.series.findIndex(
|
||||
(s, i) => !w.globals.collapsedSeriesIndices.includes(i)
|
||||
)
|
||||
this.create(e, this, firstVisibleSeries, j, opt.ttItems)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deactivateHoverFilter() {
|
||||
let w = this.w
|
||||
let graphics = new Graphics(this.ctx)
|
||||
|
||||
let allPaths = w.globals.dom.Paper.select(`.apexcharts-bar-area`)
|
||||
|
||||
for (let b = 0; b < allPaths.length; b++) {
|
||||
graphics.pathMouseLeave(allPaths[b])
|
||||
}
|
||||
}
|
||||
|
||||
handleMouseOut(opt) {
|
||||
const w = this.w
|
||||
|
||||
const xcrosshairs = this.getElXCrosshairs()
|
||||
|
||||
opt.tooltipEl.classList.remove('apexcharts-active')
|
||||
this.deactivateHoverFilter()
|
||||
if (w.config.chart.type !== 'bubble') {
|
||||
this.marker.resetPointsSize()
|
||||
}
|
||||
if (xcrosshairs !== null) {
|
||||
xcrosshairs.classList.remove('apexcharts-active')
|
||||
}
|
||||
if (this.ycrosshairs !== null) {
|
||||
this.ycrosshairs.classList.remove('apexcharts-active')
|
||||
}
|
||||
if (this.isXAxisTooltipEnabled) {
|
||||
this.xaxisTooltip.classList.remove('apexcharts-active')
|
||||
}
|
||||
if (this.yaxisTooltips.length) {
|
||||
if (this.yaxisTTEls === null) {
|
||||
this.yaxisTTEls = w.globals.dom.baseEl.querySelectorAll(
|
||||
'.apexcharts-yaxistooltip'
|
||||
)
|
||||
}
|
||||
for (let i = 0; i < this.yaxisTTEls.length; i++) {
|
||||
this.yaxisTTEls[i].classList.remove('apexcharts-active')
|
||||
}
|
||||
}
|
||||
|
||||
if (w.config.legend.tooltipHoverFormatter) {
|
||||
this.legendLabels.forEach((l) => {
|
||||
const defaultText = l.getAttribute('data:default-text')
|
||||
l.innerHTML = decodeURIComponent(defaultText)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
markerClick(e, seriesIndex, dataPointIndex) {
|
||||
const w = this.w
|
||||
if (typeof w.config.chart.events.markerClick === 'function') {
|
||||
w.config.chart.events.markerClick(e, this.ctx, {
|
||||
seriesIndex,
|
||||
dataPointIndex,
|
||||
w,
|
||||
})
|
||||
}
|
||||
this.ctx.events.fireEvent('markerClick', [
|
||||
e,
|
||||
this.ctx,
|
||||
{ seriesIndex, dataPointIndex, w },
|
||||
])
|
||||
}
|
||||
|
||||
create(e, context, capturedSeries, j, ttItems, shared = null) {
|
||||
let w = this.w
|
||||
let ttCtx = context
|
||||
|
||||
if (e.type === 'mouseup') {
|
||||
this.markerClick(e, capturedSeries, j)
|
||||
}
|
||||
|
||||
if (shared === null) shared = this.tConfig.shared
|
||||
|
||||
const hasMarkers = this.tooltipUtil.hasMarkers(capturedSeries)
|
||||
|
||||
const bars = this.tooltipUtil.getElBars()
|
||||
|
||||
if (w.config.legend.tooltipHoverFormatter) {
|
||||
let legendFormatter = w.config.legend.tooltipHoverFormatter
|
||||
|
||||
let els = Array.from(this.legendLabels)
|
||||
|
||||
// reset all legend values first
|
||||
els.forEach((l) => {
|
||||
const legendName = l.getAttribute('data:default-text')
|
||||
l.innerHTML = decodeURIComponent(legendName)
|
||||
})
|
||||
|
||||
// for irregular time series
|
||||
for (let i = 0; i < els.length; i++) {
|
||||
const l = els[i]
|
||||
const lsIndex = parseInt(l.getAttribute('i'), 10)
|
||||
const legendName = decodeURIComponent(
|
||||
l.getAttribute('data:default-text')
|
||||
)
|
||||
|
||||
let text = legendFormatter(legendName, {
|
||||
seriesIndex: shared ? lsIndex : capturedSeries,
|
||||
dataPointIndex: j,
|
||||
w,
|
||||
})
|
||||
|
||||
if (!shared) {
|
||||
l.innerHTML = lsIndex === capturedSeries ? text : legendName
|
||||
if (capturedSeries === lsIndex) {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
l.innerHTML =
|
||||
w.globals.collapsedSeriesIndices.indexOf(lsIndex) < 0
|
||||
? text
|
||||
: legendName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const commonSeriesTextsParams = {
|
||||
ttItems,
|
||||
i: capturedSeries,
|
||||
j,
|
||||
...(typeof w.globals.seriesRange?.[capturedSeries]?.[j]?.y[0]?.y1 !==
|
||||
'undefined' && {
|
||||
y1: w.globals.seriesRange?.[capturedSeries]?.[j]?.y[0]?.y1,
|
||||
}),
|
||||
...(typeof w.globals.seriesRange?.[capturedSeries]?.[j]?.y[0]?.y2 !==
|
||||
'undefined' && {
|
||||
y2: w.globals.seriesRange?.[capturedSeries]?.[j]?.y[0]?.y2,
|
||||
}),
|
||||
}
|
||||
if (shared) {
|
||||
ttCtx.tooltipLabels.drawSeriesTexts({
|
||||
...commonSeriesTextsParams,
|
||||
shared: this.showOnIntersect ? false : this.tConfig.shared,
|
||||
})
|
||||
|
||||
if (hasMarkers) {
|
||||
if (w.globals.markers.largestSize > 0) {
|
||||
ttCtx.marker.enlargePoints(j)
|
||||
} else {
|
||||
ttCtx.tooltipPosition.moveDynamicPointsOnHover(j)
|
||||
}
|
||||
} else if (this.tooltipUtil.hasBars()) {
|
||||
this.barSeriesHeight = this.tooltipUtil.getBarsHeight(bars)
|
||||
if (this.barSeriesHeight > 0) {
|
||||
// hover state, activate snap filter
|
||||
let graphics = new Graphics(this.ctx)
|
||||
let paths = w.globals.dom.Paper.select(
|
||||
`.apexcharts-bar-area[j='${j}']`
|
||||
)
|
||||
|
||||
// de-activate first
|
||||
this.deactivateHoverFilter()
|
||||
|
||||
this.tooltipPosition.moveStickyTooltipOverBars(j, capturedSeries)
|
||||
|
||||
for (let b = 0; b < paths.length; b++) {
|
||||
graphics.pathMouseEnter(paths[b])
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ttCtx.tooltipLabels.drawSeriesTexts({
|
||||
shared: false,
|
||||
...commonSeriesTextsParams,
|
||||
})
|
||||
|
||||
if (this.tooltipUtil.hasBars()) {
|
||||
ttCtx.tooltipPosition.moveStickyTooltipOverBars(j, capturedSeries)
|
||||
}
|
||||
|
||||
if (hasMarkers) {
|
||||
ttCtx.tooltipPosition.moveMarkers(capturedSeries, j)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+356
@@ -0,0 +1,356 @@
|
||||
import Utilities from '../../utils/Utils'
|
||||
|
||||
/**
|
||||
* ApexCharts Tooltip.Utils Class to support Tooltip functionality.
|
||||
*
|
||||
* @module Tooltip.Utils
|
||||
**/
|
||||
|
||||
export default class Utils {
|
||||
constructor(tooltipContext) {
|
||||
this.w = tooltipContext.w
|
||||
this.ttCtx = tooltipContext
|
||||
this.ctx = tooltipContext.ctx
|
||||
}
|
||||
|
||||
/**
|
||||
** When hovering over series, you need to capture which series is being hovered on.
|
||||
** This function will return both capturedseries index as well as inner index of that series
|
||||
* @memberof Utils
|
||||
* @param {object}
|
||||
* - hoverArea = the rect on which user hovers
|
||||
* - elGrid = dimensions of the hover rect (it can be different than hoverarea)
|
||||
*/
|
||||
getNearestValues({ hoverArea, elGrid, clientX, clientY }) {
|
||||
let w = this.w
|
||||
|
||||
const seriesBound = elGrid.getBoundingClientRect()
|
||||
const hoverWidth = seriesBound.width
|
||||
const hoverHeight = seriesBound.height
|
||||
|
||||
let xDivisor = hoverWidth / (w.globals.dataPoints - 1)
|
||||
let yDivisor = hoverHeight / w.globals.dataPoints
|
||||
|
||||
const hasBars = this.hasBars()
|
||||
|
||||
if (
|
||||
(w.globals.comboCharts || hasBars) &&
|
||||
!w.config.xaxis.convertedCatToNumeric
|
||||
) {
|
||||
xDivisor = hoverWidth / w.globals.dataPoints
|
||||
}
|
||||
|
||||
let hoverX = clientX - seriesBound.left - w.globals.barPadForNumericAxis
|
||||
let hoverY = clientY - seriesBound.top
|
||||
|
||||
const notInRect =
|
||||
hoverX < 0 || hoverY < 0 || hoverX > hoverWidth || hoverY > hoverHeight
|
||||
|
||||
if (notInRect) {
|
||||
hoverArea.classList.remove('hovering-zoom')
|
||||
hoverArea.classList.remove('hovering-pan')
|
||||
} else {
|
||||
if (w.globals.zoomEnabled) {
|
||||
hoverArea.classList.remove('hovering-pan')
|
||||
hoverArea.classList.add('hovering-zoom')
|
||||
} else if (w.globals.panEnabled) {
|
||||
hoverArea.classList.remove('hovering-zoom')
|
||||
hoverArea.classList.add('hovering-pan')
|
||||
}
|
||||
}
|
||||
|
||||
let j = Math.round(hoverX / xDivisor)
|
||||
let jHorz = Math.floor(hoverY / yDivisor)
|
||||
|
||||
if (hasBars && !w.config.xaxis.convertedCatToNumeric) {
|
||||
j = Math.ceil(hoverX / xDivisor)
|
||||
j = j - 1
|
||||
}
|
||||
|
||||
let capturedSeries = null
|
||||
let closest = null
|
||||
|
||||
let seriesXValArr = w.globals.seriesXvalues.map((seriesXVal) => {
|
||||
return seriesXVal.filter((s) => Utilities.isNumber(s))
|
||||
})
|
||||
let seriesYValArr = w.globals.seriesYvalues.map((seriesYVal) => {
|
||||
return seriesYVal.filter((s) => Utilities.isNumber(s))
|
||||
})
|
||||
|
||||
// if X axis type is not category and tooltip is not shared, then we need to find the cursor position and get the nearest value
|
||||
if (w.globals.isXNumeric) {
|
||||
// Change origin of cursor position so that we can compute the relative nearest point to the cursor on our chart
|
||||
// we only need to scale because all points are relative to the bounds.left and bounds.top => origin is virtually (0, 0)
|
||||
const chartGridEl = this.ttCtx.getElGrid()
|
||||
const chartGridElBoundingRect = chartGridEl.getBoundingClientRect()
|
||||
const transformedHoverX =
|
||||
hoverX * (chartGridElBoundingRect.width / hoverWidth)
|
||||
const transformedHoverY =
|
||||
hoverY * (chartGridElBoundingRect.height / hoverHeight)
|
||||
|
||||
closest = this.closestInMultiArray(
|
||||
transformedHoverX,
|
||||
transformedHoverY,
|
||||
seriesXValArr,
|
||||
seriesYValArr
|
||||
)
|
||||
capturedSeries = closest.index
|
||||
j = closest.j
|
||||
|
||||
if (capturedSeries !== null) {
|
||||
// initial push, it should be a little smaller than the 1st val
|
||||
seriesXValArr = w.globals.seriesXvalues[capturedSeries]
|
||||
|
||||
closest = this.closestInArray(transformedHoverX, seriesXValArr)
|
||||
|
||||
j = closest.index
|
||||
}
|
||||
}
|
||||
|
||||
w.globals.capturedSeriesIndex =
|
||||
capturedSeries === null ? -1 : capturedSeries
|
||||
|
||||
if (!j || j < 1) j = 0
|
||||
|
||||
if (w.globals.isBarHorizontal) {
|
||||
w.globals.capturedDataPointIndex = jHorz
|
||||
} else {
|
||||
w.globals.capturedDataPointIndex = j
|
||||
}
|
||||
|
||||
return {
|
||||
capturedSeries,
|
||||
j: w.globals.isBarHorizontal ? jHorz : j,
|
||||
hoverX,
|
||||
hoverY,
|
||||
}
|
||||
}
|
||||
|
||||
closestInMultiArray(hoverX, hoverY, Xarrays, Yarrays) {
|
||||
let w = this.w
|
||||
let activeIndex = 0
|
||||
let currIndex = null
|
||||
let j = -1
|
||||
|
||||
if (w.globals.series.length > 1) {
|
||||
activeIndex = this.getFirstActiveXArray(Xarrays)
|
||||
} else {
|
||||
currIndex = 0
|
||||
}
|
||||
|
||||
let currX = Xarrays[activeIndex][0]
|
||||
let diffX = Math.abs(hoverX - currX)
|
||||
|
||||
// find nearest point on x-axis
|
||||
Xarrays.forEach((arrX) => {
|
||||
arrX.forEach((x, iX) => {
|
||||
const newDiff = Math.abs(hoverX - x)
|
||||
if (newDiff <= diffX) {
|
||||
diffX = newDiff
|
||||
j = iX
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
if (j !== -1) {
|
||||
// find nearest graph on y-axis relevanted to nearest point on x-axis
|
||||
let currY = Yarrays[activeIndex][j]
|
||||
let diffY = Math.abs(hoverY - currY)
|
||||
currIndex = activeIndex
|
||||
|
||||
Yarrays.forEach((arrY, iAY) => {
|
||||
const newDiff = Math.abs(hoverY - arrY[j])
|
||||
if (newDiff <= diffY) {
|
||||
diffY = newDiff
|
||||
currIndex = iAY
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
index: currIndex,
|
||||
j,
|
||||
}
|
||||
}
|
||||
|
||||
getFirstActiveXArray(Xarrays) {
|
||||
const w = this.w
|
||||
let activeIndex = 0
|
||||
|
||||
let firstActiveSeriesIndex = Xarrays.map((xarr, index) => {
|
||||
return xarr.length > 0 ? index : -1
|
||||
})
|
||||
|
||||
for (let a = 0; a < firstActiveSeriesIndex.length; a++) {
|
||||
if (
|
||||
firstActiveSeriesIndex[a] !== -1 &&
|
||||
w.globals.collapsedSeriesIndices.indexOf(a) === -1 &&
|
||||
w.globals.ancillaryCollapsedSeriesIndices.indexOf(a) === -1
|
||||
) {
|
||||
activeIndex = firstActiveSeriesIndex[a]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return activeIndex
|
||||
}
|
||||
|
||||
closestInArray(val, arr) {
|
||||
let curr = arr[0]
|
||||
let currIndex = null
|
||||
let diff = Math.abs(val - curr)
|
||||
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
let newdiff = Math.abs(val - arr[i])
|
||||
if (newdiff < diff) {
|
||||
diff = newdiff
|
||||
currIndex = i
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
index: currIndex,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When there are multiple series, it is possible to have different x values for each series.
|
||||
* But it may be possible in those multiple series, that there is same x value for 2 or more
|
||||
* series.
|
||||
* @memberof Utils
|
||||
* @param {int}
|
||||
* - j = is the inner index of series -> (series[i][j])
|
||||
* @return {bool}
|
||||
*/
|
||||
isXoverlap(j) {
|
||||
let w = this.w
|
||||
let xSameForAllSeriesJArr = []
|
||||
|
||||
const seriesX = w.globals.seriesX.filter((s) => typeof s[0] !== 'undefined')
|
||||
|
||||
if (seriesX.length > 0) {
|
||||
for (let i = 0; i < seriesX.length - 1; i++) {
|
||||
if (
|
||||
typeof seriesX[i][j] !== 'undefined' &&
|
||||
typeof seriesX[i + 1][j] !== 'undefined'
|
||||
) {
|
||||
if (seriesX[i][j] !== seriesX[i + 1][j]) {
|
||||
xSameForAllSeriesJArr.push('unEqual')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (xSameForAllSeriesJArr.length === 0) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
isInitialSeriesSameLen() {
|
||||
let sameLen = true
|
||||
|
||||
const initialSeries = this.w.globals.initialSeries
|
||||
|
||||
for (let i = 0; i < initialSeries.length - 1; i++) {
|
||||
if (initialSeries[i].data.length !== initialSeries[i + 1].data.length) {
|
||||
sameLen = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return sameLen
|
||||
}
|
||||
|
||||
getBarsHeight(allbars) {
|
||||
let bars = [...allbars]
|
||||
const totalHeight = bars.reduce((acc, bar) => acc + bar.getBBox().height, 0)
|
||||
|
||||
return totalHeight
|
||||
}
|
||||
|
||||
getElMarkers(capturedSeries) {
|
||||
// The selector .apexcharts-series-markers-wrap > * includes marker groups for which the
|
||||
// .apexcharts-series-markers class is not added due to null values or discrete markers
|
||||
if (typeof capturedSeries == 'number') {
|
||||
return this.w.globals.dom.baseEl.querySelectorAll(
|
||||
`.apexcharts-series[data\\:realIndex='${capturedSeries}'] .apexcharts-series-markers-wrap > *`
|
||||
)
|
||||
}
|
||||
return this.w.globals.dom.baseEl.querySelectorAll(
|
||||
'.apexcharts-series-markers-wrap > *'
|
||||
)
|
||||
}
|
||||
|
||||
getAllMarkers() {
|
||||
// first get all marker parents. This parent class contains series-index
|
||||
// which helps to sort the markers as they are dynamic
|
||||
let markersWraps = this.w.globals.dom.baseEl.querySelectorAll(
|
||||
'.apexcharts-series-markers-wrap'
|
||||
)
|
||||
|
||||
markersWraps = [...markersWraps]
|
||||
markersWraps.sort((a, b) => {
|
||||
var indexA = Number(a.getAttribute('data:realIndex'))
|
||||
var indexB = Number(b.getAttribute('data:realIndex'))
|
||||
return indexB < indexA ? 1 : indexB > indexA ? -1 : 0
|
||||
})
|
||||
|
||||
let markers = []
|
||||
markersWraps.forEach((m) => {
|
||||
markers.push(m.querySelector('.apexcharts-marker'))
|
||||
})
|
||||
|
||||
return markers
|
||||
}
|
||||
|
||||
hasMarkers(capturedSeries) {
|
||||
const markers = this.getElMarkers(capturedSeries)
|
||||
return markers.length > 0
|
||||
}
|
||||
|
||||
getElBars() {
|
||||
return this.w.globals.dom.baseEl.querySelectorAll(
|
||||
'.apexcharts-bar-series, .apexcharts-candlestick-series, .apexcharts-boxPlot-series, .apexcharts-rangebar-series'
|
||||
)
|
||||
}
|
||||
|
||||
hasBars() {
|
||||
const bars = this.getElBars()
|
||||
return bars.length > 0
|
||||
}
|
||||
|
||||
getHoverMarkerSize(index) {
|
||||
const w = this.w
|
||||
let hoverSize = w.config.markers.hover.size
|
||||
|
||||
if (hoverSize === undefined) {
|
||||
hoverSize =
|
||||
w.globals.markers.size[index] + w.config.markers.hover.sizeOffset
|
||||
}
|
||||
return hoverSize
|
||||
}
|
||||
|
||||
toggleAllTooltipSeriesGroups(state) {
|
||||
let w = this.w
|
||||
const ttCtx = this.ttCtx
|
||||
|
||||
if (ttCtx.allTooltipSeriesGroups.length === 0) {
|
||||
ttCtx.allTooltipSeriesGroups = w.globals.dom.baseEl.querySelectorAll(
|
||||
'.apexcharts-tooltip-series-group'
|
||||
)
|
||||
}
|
||||
|
||||
let allTooltipSeriesGroups = ttCtx.allTooltipSeriesGroups
|
||||
for (let i = 0; i < allTooltipSeriesGroups.length; i++) {
|
||||
if (state === 'enable') {
|
||||
allTooltipSeriesGroups[i].classList.add('apexcharts-active')
|
||||
allTooltipSeriesGroups[i].style.display = w.config.tooltip.items.display
|
||||
} else {
|
||||
allTooltipSeriesGroups[i].classList.remove('apexcharts-active')
|
||||
allTooltipSeriesGroups[i].style.display = 'none'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user