feat:Added piechart in Dashboard

This commit is contained in:
2025-02-22 15:35:48 +05:30
parent 357071b967
commit f7cb1af2c4
384 changed files with 112765 additions and 8 deletions
+284
View File
@@ -0,0 +1,284 @@
import Graphics from '../Graphics'
import Utils from '../../utils/Utils'
export default class Helpers {
constructor(lgCtx) {
this.w = lgCtx.w
this.lgCtx = lgCtx
}
getLegendStyles() {
let stylesheet = document.createElement('style')
stylesheet.setAttribute('type', 'text/css')
const nonce = this.lgCtx.ctx?.opts?.chart?.nonce || this.w.config.chart.nonce;
if (nonce) {
stylesheet.setAttribute('nonce', nonce);
}
const text = `
.apexcharts-legend {
display: flex;
overflow: auto;
padding: 0 10px;
}
.apexcharts-legend.apx-legend-position-bottom, .apexcharts-legend.apx-legend-position-top {
flex-wrap: wrap
}
.apexcharts-legend.apx-legend-position-right, .apexcharts-legend.apx-legend-position-left {
flex-direction: column;
bottom: 0;
}
.apexcharts-legend.apx-legend-position-bottom.apexcharts-align-left, .apexcharts-legend.apx-legend-position-top.apexcharts-align-left, .apexcharts-legend.apx-legend-position-right, .apexcharts-legend.apx-legend-position-left {
justify-content: flex-start;
}
.apexcharts-legend.apx-legend-position-bottom.apexcharts-align-center, .apexcharts-legend.apx-legend-position-top.apexcharts-align-center {
justify-content: center;
}
.apexcharts-legend.apx-legend-position-bottom.apexcharts-align-right, .apexcharts-legend.apx-legend-position-top.apexcharts-align-right {
justify-content: flex-end;
}
.apexcharts-legend-series {
cursor: pointer;
line-height: normal;
}
.apexcharts-legend.apx-legend-position-bottom .apexcharts-legend-series, .apexcharts-legend.apx-legend-position-top .apexcharts-legend-series{
display: flex;
align-items: center;
}
.apexcharts-legend-text {
position: relative;
font-size: 14px;
}
.apexcharts-legend-text *, .apexcharts-legend-marker * {
pointer-events: none;
}
.apexcharts-legend-marker {
position: relative;
display: inline-block;
cursor: pointer;
margin-right: 3px;
border-style: solid;
}
.apexcharts-legend.apexcharts-align-right .apexcharts-legend-series, .apexcharts-legend.apexcharts-align-left .apexcharts-legend-series{
display: inline-block;
}
.apexcharts-legend-series.apexcharts-no-click {
cursor: auto;
}
.apexcharts-legend .apexcharts-hidden-zero-series, .apexcharts-legend .apexcharts-hidden-null-series {
display: none !important;
}
.apexcharts-inactive-legend {
opacity: 0.45;
}`
let rules = document.createTextNode(text)
stylesheet.appendChild(rules)
return stylesheet
}
getLegendBBox() {
const w = this.w
let currLegendsWrap = w.globals.dom.baseEl.querySelector(
'.apexcharts-legend'
)
let currLegendsWrapRect = currLegendsWrap.getBoundingClientRect()
let currLegendsWrapWidth = currLegendsWrapRect.width
let currLegendsWrapHeight = currLegendsWrapRect.height
return {
clwh: currLegendsWrapHeight,
clww: currLegendsWrapWidth
}
}
appendToForeignObject() {
const gl = this.w.globals
gl.dom.elLegendForeign.appendChild(this.getLegendStyles())
}
toggleDataSeries(seriesCnt, isHidden) {
const w = this.w
if (w.globals.axisCharts || w.config.chart.type === 'radialBar') {
w.globals.resized = true // we don't want initial animations again
let seriesEl = null
let realIndex = null
// yes, make it null. 1 series will rise at a time
w.globals.risingSeries = []
if (w.globals.axisCharts) {
seriesEl = w.globals.dom.baseEl.querySelector(
`.apexcharts-series[data\\:realIndex='${seriesCnt}']`
)
realIndex = parseInt(seriesEl.getAttribute('data:realIndex'), 10)
} else {
seriesEl = w.globals.dom.baseEl.querySelector(
`.apexcharts-series[rel='${seriesCnt + 1}']`
)
realIndex = parseInt(seriesEl.getAttribute('rel'), 10) - 1
}
if (isHidden) {
const seriesToMakeVisible = [
{
cs: w.globals.collapsedSeries,
csi: w.globals.collapsedSeriesIndices
},
{
cs: w.globals.ancillaryCollapsedSeries,
csi: w.globals.ancillaryCollapsedSeriesIndices
}
]
seriesToMakeVisible.forEach((r) => {
this.riseCollapsedSeries(r.cs, r.csi, realIndex)
})
} else {
this.hideSeries({ seriesEl, realIndex })
}
} else {
// for non-axis charts i.e pie / donuts
let seriesEl = w.globals.dom.Paper.select(
` .apexcharts-series[rel='${seriesCnt + 1}'] path`
)
const type = w.config.chart.type
if (type === 'pie' || type === 'polarArea' || type === 'donut') {
let dataLabels = w.config.plotOptions.pie.donut.labels
const graphics = new Graphics(this.lgCtx.ctx)
graphics.pathMouseDown(seriesEl.members[0], null)
this.lgCtx.ctx.pie.printDataLabelsInner(
seriesEl.members[0].node,
dataLabels
)
}
seriesEl.fire('click')
}
}
hideSeries({ seriesEl, realIndex }) {
const w = this.w
let series = Utils.clone(w.config.series)
if (w.globals.axisCharts) {
let shouldNotHideYAxis = false
if (
w.config.yaxis[realIndex] &&
w.config.yaxis[realIndex].show &&
w.config.yaxis[realIndex].showAlways
) {
shouldNotHideYAxis = true
if (w.globals.ancillaryCollapsedSeriesIndices.indexOf(realIndex) < 0) {
w.globals.ancillaryCollapsedSeries.push({
index: realIndex,
data: series[realIndex].data.slice(),
type: seriesEl.parentNode.className.baseVal.split('-')[1]
})
w.globals.ancillaryCollapsedSeriesIndices.push(realIndex)
}
}
if (!shouldNotHideYAxis) {
w.globals.collapsedSeries.push({
index: realIndex,
data: series[realIndex].data.slice(),
type: seriesEl.parentNode.className.baseVal.split('-')[1]
})
w.globals.collapsedSeriesIndices.push(realIndex)
let removeIndexOfRising = w.globals.risingSeries.indexOf(realIndex)
w.globals.risingSeries.splice(removeIndexOfRising, 1)
}
} else {
w.globals.collapsedSeries.push({
index: realIndex,
data: series[realIndex]
})
w.globals.collapsedSeriesIndices.push(realIndex)
}
let seriesChildren = seriesEl.childNodes
for (let sc = 0; sc < seriesChildren.length; sc++) {
if (
seriesChildren[sc].classList.contains('apexcharts-series-markers-wrap')
) {
if (seriesChildren[sc].classList.contains('apexcharts-hide')) {
seriesChildren[sc].classList.remove('apexcharts-hide')
} else {
seriesChildren[sc].classList.add('apexcharts-hide')
}
}
}
w.globals.allSeriesCollapsed =
w.globals.collapsedSeries.length === w.config.series.length
series = this._getSeriesBasedOnCollapsedState(series)
this.lgCtx.ctx.updateHelpers._updateSeries(
series,
w.config.chart.animations.dynamicAnimation.enabled
)
}
riseCollapsedSeries(collapsedSeries, seriesIndices, realIndex) {
const w = this.w
let series = Utils.clone(w.config.series)
if (collapsedSeries.length > 0) {
for (let c = 0; c < collapsedSeries.length; c++) {
if (collapsedSeries[c].index === realIndex) {
if (w.globals.axisCharts) {
series[realIndex].data = collapsedSeries[c].data.slice()
collapsedSeries.splice(c, 1)
seriesIndices.splice(c, 1)
w.globals.risingSeries.push(realIndex)
} else {
series[realIndex] = collapsedSeries[c].data
collapsedSeries.splice(c, 1)
seriesIndices.splice(c, 1)
w.globals.risingSeries.push(realIndex)
}
}
}
series = this._getSeriesBasedOnCollapsedState(series)
this.lgCtx.ctx.updateHelpers._updateSeries(
series,
w.config.chart.animations.dynamicAnimation.enabled
)
}
}
_getSeriesBasedOnCollapsedState(series) {
const w = this.w
if (w.globals.axisCharts) {
series.forEach((s, sI) => {
if (w.globals.collapsedSeriesIndices.indexOf(sI) > -1) {
series[sI].data = []
}
})
} else {
series.forEach((s, sI) => {
if (w.globals.collapsedSeriesIndices.indexOf(sI) > -1) {
series[sI] = 0
}
})
}
return series
}
}
+478
View File
@@ -0,0 +1,478 @@
import CoreUtils from '../CoreUtils'
import Dimensions from '../dimensions/Dimensions'
import Graphics from '../Graphics'
import Series from '../Series'
import Utils from '../../utils/Utils'
import Helpers from './Helpers'
/**
* ApexCharts Legend Class to draw legend.
*
* @module Legend
**/
class Legend {
constructor(ctx) {
this.ctx = ctx
this.w = ctx.w
this.onLegendClick = this.onLegendClick.bind(this)
this.onLegendHovered = this.onLegendHovered.bind(this)
this.isBarsDistributed =
this.w.config.chart.type === 'bar' &&
this.w.config.plotOptions.bar.distributed &&
this.w.config.series.length === 1
this.legendHelpers = new Helpers(this)
}
init() {
const w = this.w
const gl = w.globals
const cnf = w.config
const showLegendAlways =
(cnf.legend.showForSingleSeries && gl.series.length === 1) ||
this.isBarsDistributed ||
gl.series.length > 1
if ((showLegendAlways || !gl.axisCharts) && cnf.legend.show) {
while (gl.dom.elLegendWrap.firstChild) {
gl.dom.elLegendWrap.removeChild(gl.dom.elLegendWrap.firstChild)
}
this.drawLegends()
if (!Utils.isIE11()) {
this.legendHelpers.appendToForeignObject()
} else {
// IE11 doesn't supports foreignObject, hence append it to <head>
document
.getElementsByTagName('head')[0]
.appendChild(this.legendHelpers.getLegendStyles())
}
if (cnf.legend.position === 'bottom' || cnf.legend.position === 'top') {
this.legendAlignHorizontal()
} else if (
cnf.legend.position === 'right' ||
cnf.legend.position === 'left'
) {
this.legendAlignVertical()
}
}
}
drawLegends() {
let me = this
let w = this.w
let fontFamily = w.config.legend.fontFamily
let legendNames = w.globals.seriesNames
let fillcolor = w.globals.colors.slice()
if (w.config.chart.type === 'heatmap') {
const ranges = w.config.plotOptions.heatmap.colorScale.ranges
legendNames = ranges.map((colorScale) => {
return colorScale.name
? colorScale.name
: colorScale.from + ' - ' + colorScale.to
})
fillcolor = ranges.map((color) => color.color)
} else if (this.isBarsDistributed) {
legendNames = w.globals.labels.slice()
}
if (w.config.legend.customLegendItems.length) {
legendNames = w.config.legend.customLegendItems
}
let legendFormatter = w.globals.legendFormatter
let isLegendInversed = w.config.legend.inverseOrder
for (
let i = isLegendInversed ? legendNames.length - 1 : 0;
isLegendInversed ? i >= 0 : i <= legendNames.length - 1;
isLegendInversed ? i-- : i++
) {
let text = legendFormatter(legendNames[i], { seriesIndex: i, w })
let collapsedSeries = false
let ancillaryCollapsedSeries = false
if (w.globals.collapsedSeries.length > 0) {
for (let c = 0; c < w.globals.collapsedSeries.length; c++) {
if (w.globals.collapsedSeries[c].index === i) {
collapsedSeries = true
}
}
}
if (w.globals.ancillaryCollapsedSeriesIndices.length > 0) {
for (
let c = 0;
c < w.globals.ancillaryCollapsedSeriesIndices.length;
c++
) {
if (w.globals.ancillaryCollapsedSeriesIndices[c] === i) {
ancillaryCollapsedSeries = true
}
}
}
let elMarker = document.createElement('span')
elMarker.classList.add('apexcharts-legend-marker')
let mOffsetX = w.config.legend.markers.offsetX
let mOffsetY = w.config.legend.markers.offsetY
let mHeight = w.config.legend.markers.height
let mWidth = w.config.legend.markers.width
let mBorderWidth = w.config.legend.markers.strokeWidth
let mBorderColor = w.config.legend.markers.strokeColor
let mBorderRadius = w.config.legend.markers.radius
let mStyle = elMarker.style
mStyle.background = fillcolor[i]
mStyle.color = fillcolor[i]
mStyle.setProperty('background', fillcolor[i], 'important')
// override fill color with custom legend.markers.fillColors
if (
w.config.legend.markers.fillColors &&
w.config.legend.markers.fillColors[i]
) {
mStyle.background = w.config.legend.markers.fillColors[i]
}
// override with data color
if (w.globals.seriesColors[i] !== undefined) {
mStyle.background = w.globals.seriesColors[i]
mStyle.color = w.globals.seriesColors[i]
}
mStyle.height = Array.isArray(mHeight)
? parseFloat(mHeight[i]) + 'px'
: parseFloat(mHeight) + 'px'
mStyle.width = Array.isArray(mWidth)
? parseFloat(mWidth[i]) + 'px'
: parseFloat(mWidth) + 'px'
mStyle.left =
(Array.isArray(mOffsetX)
? parseFloat(mOffsetX[i])
: parseFloat(mOffsetX)) + 'px'
mStyle.top =
(Array.isArray(mOffsetY)
? parseFloat(mOffsetY[i])
: parseFloat(mOffsetY)) + 'px'
mStyle.borderWidth = Array.isArray(mBorderWidth)
? mBorderWidth[i]
: mBorderWidth
mStyle.borderColor = Array.isArray(mBorderColor)
? mBorderColor[i]
: mBorderColor
mStyle.borderRadius = Array.isArray(mBorderRadius)
? parseFloat(mBorderRadius[i]) + 'px'
: parseFloat(mBorderRadius) + 'px'
if (w.config.legend.markers.customHTML) {
if (Array.isArray(w.config.legend.markers.customHTML)) {
if (w.config.legend.markers.customHTML[i]) {
elMarker.innerHTML = w.config.legend.markers.customHTML[i]()
}
} else {
elMarker.innerHTML = w.config.legend.markers.customHTML()
}
}
Graphics.setAttrs(elMarker, {
rel: i + 1,
'data:collapsed': collapsedSeries || ancillaryCollapsedSeries,
})
if (collapsedSeries || ancillaryCollapsedSeries) {
elMarker.classList.add('apexcharts-inactive-legend')
}
let elLegend = document.createElement('div')
let elLegendText = document.createElement('span')
elLegendText.classList.add('apexcharts-legend-text')
elLegendText.innerHTML = Array.isArray(text) ? text.join(' ') : text
let textColor = w.config.legend.labels.useSeriesColors
? w.globals.colors[i]
: Array.isArray(w.config.legend.labels.colors)
? w.config.legend.labels.colors?.[i]
: w.config.legend.labels.colors
if (!textColor) {
textColor = w.config.chart.foreColor
}
elLegendText.style.color = textColor
elLegendText.style.fontSize = parseFloat(w.config.legend.fontSize) + 'px'
elLegendText.style.fontWeight = w.config.legend.fontWeight
elLegendText.style.fontFamily = fontFamily || w.config.chart.fontFamily
Graphics.setAttrs(elLegendText, {
rel: i + 1,
i,
'data:default-text': encodeURIComponent(text),
'data:collapsed': collapsedSeries || ancillaryCollapsedSeries,
})
elLegend.appendChild(elMarker)
elLegend.appendChild(elLegendText)
const coreUtils = new CoreUtils(this.ctx)
if (!w.config.legend.showForZeroSeries) {
const total = coreUtils.getSeriesTotalByIndex(i)
if (
total === 0 &&
coreUtils.seriesHaveSameValues(i) &&
!coreUtils.isSeriesNull(i) &&
w.globals.collapsedSeriesIndices.indexOf(i) === -1 &&
w.globals.ancillaryCollapsedSeriesIndices.indexOf(i) === -1
) {
elLegend.classList.add('apexcharts-hidden-zero-series')
}
}
if (!w.config.legend.showForNullSeries) {
if (
coreUtils.isSeriesNull(i) &&
w.globals.collapsedSeriesIndices.indexOf(i) === -1 &&
w.globals.ancillaryCollapsedSeriesIndices.indexOf(i) === -1
) {
elLegend.classList.add('apexcharts-hidden-null-series')
}
}
w.globals.dom.elLegendWrap.appendChild(elLegend)
w.globals.dom.elLegendWrap.classList.add(
`apexcharts-align-${w.config.legend.horizontalAlign}`
)
w.globals.dom.elLegendWrap.classList.add(
'apx-legend-position-' + w.config.legend.position
)
elLegend.classList.add('apexcharts-legend-series')
elLegend.style.margin = `${w.config.legend.itemMargin.vertical}px ${w.config.legend.itemMargin.horizontal}px`
w.globals.dom.elLegendWrap.style.width = w.config.legend.width
? w.config.legend.width + 'px'
: ''
w.globals.dom.elLegendWrap.style.height = w.config.legend.height
? w.config.legend.height + 'px'
: ''
Graphics.setAttrs(elLegend, {
rel: i + 1,
seriesName: Utils.escapeString(legendNames[i]),
'data:collapsed': collapsedSeries || ancillaryCollapsedSeries,
})
if (collapsedSeries || ancillaryCollapsedSeries) {
elLegend.classList.add('apexcharts-inactive-legend')
}
if (!w.config.legend.onItemClick.toggleDataSeries) {
elLegend.classList.add('apexcharts-no-click')
}
}
w.globals.dom.elWrap.addEventListener('click', me.onLegendClick, true)
if (
w.config.legend.onItemHover.highlightDataSeries &&
w.config.legend.customLegendItems.length === 0
) {
w.globals.dom.elWrap.addEventListener(
'mousemove',
me.onLegendHovered,
true
)
w.globals.dom.elWrap.addEventListener(
'mouseout',
me.onLegendHovered,
true
)
}
}
setLegendWrapXY(offsetX, offsetY) {
let w = this.w
let elLegendWrap = w.globals.dom.elLegendWrap
const legendRect = elLegendWrap.getBoundingClientRect()
let x = 0
let y = 0
if (w.config.legend.position === 'bottom') {
y = y + (w.globals.svgHeight - legendRect.height / 2)
} else if (w.config.legend.position === 'top') {
const dim = new Dimensions(this.ctx)
const titleH = dim.dimHelpers.getTitleSubtitleCoords('title').height
const subtitleH = dim.dimHelpers.getTitleSubtitleCoords('subtitle').height
y =
y +
(titleH > 0 ? titleH - 10 : 0) +
(subtitleH > 0 ? subtitleH - 10 : 0)
}
elLegendWrap.style.position = 'absolute'
x = x + offsetX + w.config.legend.offsetX
y = y + offsetY + w.config.legend.offsetY
elLegendWrap.style.left = x + 'px'
elLegendWrap.style.top = y + 'px'
if (w.config.legend.position === 'bottom') {
elLegendWrap.style.top = 'auto'
elLegendWrap.style.bottom = 5 - w.config.legend.offsetY + 'px'
} else if (w.config.legend.position === 'right') {
elLegendWrap.style.left = 'auto'
elLegendWrap.style.right = 25 + w.config.legend.offsetX + 'px'
}
const fixedHeigthWidth = ['width', 'height']
fixedHeigthWidth.forEach((hw) => {
if (elLegendWrap.style[hw]) {
elLegendWrap.style[hw] = parseInt(w.config.legend[hw], 10) + 'px'
}
})
}
legendAlignHorizontal() {
let w = this.w
let elLegendWrap = w.globals.dom.elLegendWrap
elLegendWrap.style.right = 0
let lRect = this.legendHelpers.getLegendBBox()
let dimensions = new Dimensions(this.ctx)
let titleRect = dimensions.dimHelpers.getTitleSubtitleCoords('title')
let subtitleRect = dimensions.dimHelpers.getTitleSubtitleCoords('subtitle')
let offsetX = 20
let offsetY = 0
// the whole legend box is set to bottom
if (w.config.legend.position === 'bottom') {
offsetY = -lRect.clwh / 1.8
} else if (w.config.legend.position === 'top') {
offsetY =
titleRect.height +
subtitleRect.height +
w.config.title.margin +
w.config.subtitle.margin -
10
}
this.setLegendWrapXY(offsetX, offsetY)
}
legendAlignVertical() {
let w = this.w
let lRect = this.legendHelpers.getLegendBBox()
let offsetY = 20
let offsetX = 0
if (w.config.legend.position === 'left') {
offsetX = 20
}
if (w.config.legend.position === 'right') {
offsetX = w.globals.svgWidth - lRect.clww - 10
}
this.setLegendWrapXY(offsetX, offsetY)
}
onLegendHovered(e) {
const w = this.w
const hoverOverLegend =
e.target.classList.contains('apexcharts-legend-series') ||
e.target.classList.contains('apexcharts-legend-text') ||
e.target.classList.contains('apexcharts-legend-marker')
if (w.config.chart.type !== 'heatmap' && !this.isBarsDistributed) {
if (
!e.target.classList.contains('apexcharts-inactive-legend') &&
hoverOverLegend
) {
let series = new Series(this.ctx)
series.toggleSeriesOnHover(e, e.target)
}
} else {
// for heatmap handling
if (hoverOverLegend) {
let seriesCnt = parseInt(e.target.getAttribute('rel'), 10) - 1
this.ctx.events.fireEvent('legendHover', [this.ctx, seriesCnt, this.w])
let series = new Series(this.ctx)
series.highlightRangeInSeries(e, e.target)
}
}
}
onLegendClick(e) {
const w = this.w
if (w.config.legend.customLegendItems.length) return
if (
e.target.classList.contains('apexcharts-legend-series') ||
e.target.classList.contains('apexcharts-legend-text') ||
e.target.classList.contains('apexcharts-legend-marker')
) {
let seriesCnt = parseInt(e.target.getAttribute('rel'), 10) - 1
let isHidden = e.target.getAttribute('data:collapsed') === 'true'
const legendClick = this.w.config.chart.events.legendClick
if (typeof legendClick === 'function') {
legendClick(this.ctx, seriesCnt, this.w)
}
this.ctx.events.fireEvent('legendClick', [this.ctx, seriesCnt, this.w])
const markerClick = this.w.config.legend.markers.onClick
if (
typeof markerClick === 'function' &&
e.target.classList.contains('apexcharts-legend-marker')
) {
markerClick(this.ctx, seriesCnt, this.w)
this.ctx.events.fireEvent('legendMarkerClick', [
this.ctx,
seriesCnt,
this.w,
])
}
// for now - just prevent click on heatmap legend - and allow hover only
const clickAllowed =
w.config.chart.type !== 'treemap' &&
w.config.chart.type !== 'heatmap' &&
!this.isBarsDistributed
if (clickAllowed && w.config.legend.onItemClick.toggleDataSeries) {
this.legendHelpers.toggleDataSeries(seriesCnt, isHidden)
}
}
}
}
export default Legend