0

I am creating a donut chart using Chartist but unable to add a custom tooltip with resize slice on the mouse hover event. Chartist "Draw" method is rerender on changing the position of mouse on hover of the chart I pasted my code as below:

/* eslint-disable no-unused-vars */
import React, {useState} from 'react'
import PropTypes from 'prop-types'
import ChartistGraph from 'react-chartist'
import Chartist from 'chartist'
import styles from './MyChart.scss'
import {formatNumber} from '../../../../utils/formatter'
import MyChartInfo from './MyChartInfo'

function MyChart ({ dataSeries, getDetailsTable }) {
  const [changePercent, setChangePercent] = useState(0)
  const [showToolTip, setShowToolTip] = useState(false)
  const [myChartInfoDetails, setMyChartInfoDetails] = useState({sectorName: '', changePercent: ''})
  const [toolTipPosition, setToolTipPosition] = useState({})

  function setToolTip () {
    const PopOverData = [myChartInfoDetails.sectorName || '', `% Return: ${myChartInfoDetails.changePercent || ''}`, '(Click to view top & worst performing equities)']
    return (<MyChartInfo dataTable={PopOverData} style={toolTipPosition} />)
  }
  let pieOptions = {
    width: '520px',
    height: '520px',
    donut: true,
    donutWidth: 150,
    donutSolid: false,
    startAngle: 270,
    showLabel: true,
    chartPadding: 60,
    labelOffset: 105,
    labelDirection: 'explode',
    labelInterpolationFnc: function (value) {
      return value
    }
  }
  let pieData = {
    series: dataSeries && dataSeries.length > 0 ? dataSeries.map(item => Math.abs(item.value)) : [100],
    labels: dataSeries && dataSeries.length > 0 ? dataSeries.map(item => item.name.concat('<br>', formatNumber(item.value, {precision: 2, negSign: true, posSign: true}))) : ['']
  }
  // eslint-disable-next-line no-unused-vars
  const listner = {
    created: (chart) => {
      const chartPie = document.querySelectorAll('.ct-chart-donut .ct-slice-donut')
      chartPie && chartPie.forEach((pie) => {
        pie.addEventListener('mouseenter', (e) => {
          e.stopPropagation()
          pie.setAttribute('style', 'stroke-width: 170px;')
          console.log('hover aa  ', changePercent, Math.abs(pie.getAttribute('ct:value')))
          setChangePercent(Math.abs(pie.getAttribute('ct:value')))
          let topPosition = e.layerY + 30
          let leftPosition = e.layerX + 30
          setToolTipPosition({top: topPosition, left: leftPosition})
          const myChartData = dataSeries.find(sector => Math.abs(sector.value) === Math.abs(pie.getAttribute('ct:value')))
          setSectorDetails({sectorName: myChartData.name, changePercent: myChartData.value})
          setShowToolTip(true)
        })
        pie.addEventListener('mouseleave', (e) => {
          pie.setAttribute('style', 'stroke-width: 150px;')
          setShowToolTip(false)
        })
        pie.addEventListener('click', (e) => {
          const Id = dataSeries.find(sector => Math.abs(sector.value) === Math.abs(pie.getAttribute('ct:value'))).id.toString()
          console.log('click ', Id, pie.getAttribute('ct:value'))
          getDetailsTable(Id)
        })
      })
    },
    draw: (data) => {
      // console.log('data', data)
      if (data.type === 'slice') {
        // Get the total path length in order to use for dash array animation
        var pathLength = data.element._node.getTotalLength()

        // Set a dasharray that matches the path length as prerequisite to animate dashoffset
        data.element.attr({
          'stroke-dasharray': pathLength + 'px ' + pathLength + 'px'
        })
        let noOfNonZeros = dataSeries.length > 0 && dataSeries.filter(e => e.value > 0).length
        // Create animation definition while also assigning an ID to the animation for later sync usage
        var animationDefinition = {
          'stroke-dashoffset': {
            id: 'anim' + data.index,
            dur: 1,
            from: -pathLength + 'px',
            to: noOfNonZeros > 1 ? '2px' : '0px',
            easing: Chartist.Svg.Easing.easeOutQuint,
            // We need to use `fill: 'freeze'` otherwise our animation will fall back to initial (not visible)
            fill: 'freeze'
          }
        }

        // If this was not the first slice, we need to time the animation so that it uses the end sync event of the previous animation
        if (data.index !== 0) {
          animationDefinition['stroke-dashoffset'].begin = 'anim' + (data.index - 1) + '.end'
        }

        // We need to set an initial value before the animation starts as we are not in guided mode which would do that for us
        data.element.attr({
          'stroke-dashoffset': -pathLength + 'px'
        })

        // We can't use guided mode as the animations need to rely on setting begin manually
        // See http://gionkunz.github.io/chartist-js/api-documentation.html#chartistsvg-function-animate
        data.element.animate(animationDefinition, false)
        if (dataSeries.length === 0) {
          data.element._node.setAttribute('data-value', 'no-composition')
        }
      }
      if (data.type === 'label') {
        let textHtml = ['<p>', data.text, '</p>'].join('')
        let multilineText = Chartist.Svg('svg').foreignObject(
          textHtml,
          {
            style: 'overflow: visible;',
            x: data.x - 30,
            y: data.y - 30,
            width: 90,
            height: 30
          },
          'ct-label'
        )
        data.element.replace(multilineText)
      }
    }
  }
  return (
    <div data-testid='gPieChart' id='performance_chart'>
      {<ChartistGraph
        data={pieData}
        options={pieOptions}
        type='Pie'
        style={{ width: '100%', height: '100%' }}
        className={styles.MyChart}
        listener={listner}
      />
      }
      {showToolTip && setToolTip()}
    </div>
  )
}
MyChart.propTypes = {
  dataSeries: PropTypes.array,
  getDetailsTable: PropTypes.func
}

export default MyChart

Output

I want to add a custom tooltip with resize slice functionality on mouse hover event

0 Answers0