Your IP : 216.73.217.77


Current Path : /home/users/unlimited/www/learnoid.codeskitter.site/node_modules/apexcharts/src/charts/
Upload File :
Current File : /home/users/unlimited/www/learnoid.codeskitter.site/node_modules/apexcharts/src/charts/Treemap.js

import '../libs/Treemap-squared'
import Graphics from '../modules/Graphics'
import Animations from '../modules/Animations'
import Fill from '../modules/Fill'
import Helpers from './common/treemap/Helpers'
import Filters from '../modules/Filters'

import Utils from '../utils/Utils'

/**
 * ApexCharts TreemapChart Class.
 * @module TreemapChart
 **/

export default class TreemapChart {
  constructor(ctx, xyRatios) {
    this.ctx = ctx
    this.w = ctx.w

    this.strokeWidth = this.w.config.stroke.width
    this.helpers = new Helpers(ctx)
    this.dynamicAnim = this.w.config.chart.animations.dynamicAnimation

    this.labels = []
  }

  draw(series) {
    let w = this.w
    const graphics = new Graphics(this.ctx)
    const fill = new Fill(this.ctx)

    let ret = graphics.group({
      class: 'apexcharts-treemap',
    })

    if (w.globals.noData) return ret

    let ser = []
    series.forEach((s) => {
      let d = s.map((v) => {
        return Math.abs(v)
      })
      ser.push(d)
    })

    this.negRange = this.helpers.checkColorRange()

    w.config.series.forEach((s, i) => {
      s.data.forEach((l) => {
        if (!Array.isArray(this.labels[i])) this.labels[i] = []
        this.labels[i].push(l.x)
      })
    })

    const nodes = window.TreemapSquared.generate(
      ser,
      w.globals.gridWidth,
      w.globals.gridHeight
    )

    nodes.forEach((node, i) => {
      let elSeries = graphics.group({
        class: `apexcharts-series apexcharts-treemap-series`,
        seriesName: Utils.escapeString(w.globals.seriesNames[i]),
        rel: i + 1,
        'data:realIndex': i,
      })

      if (w.config.chart.dropShadow.enabled) {
        const shadow = w.config.chart.dropShadow
        const filters = new Filters(this.ctx)
        filters.dropShadow(ret, shadow, i)
      }

      let elDataLabelWrap = graphics.group({
        class: 'apexcharts-data-labels',
      })

      let bounds = {
        xMin: Infinity,
        yMin: Infinity,
        xMax: -Infinity,
        yMax: -Infinity,
      }

      node.forEach((r, j) => {
        const x1 = r[0]
        const y1 = r[1]
        const x2 = r[2]
        const y2 = r[3]

        bounds.xMin = Math.min(bounds.xMin, x1)
        bounds.yMin = Math.min(bounds.yMin, y1)
        bounds.xMax = Math.max(bounds.xMax, x2)
        bounds.yMax = Math.max(bounds.yMax, y2)

        let colorProps = this.helpers.getShadeColor(
          w.config.chart.type,
          i,
          j,
          this.negRange
        )
        let color = colorProps.color

        let pathFill = fill.fillPath({
          color,
          seriesNumber: i,
          dataPointIndex: j,
        })

        let elRect = graphics.drawRect(
          x1,
          y1,
          x2 - x1,
          y2 - y1,
          w.config.plotOptions.treemap.borderRadius,
          '#fff',
          1,
          this.strokeWidth,
          w.config.plotOptions.treemap.useFillColorAsStroke
            ? color
            : w.globals.stroke.colors[i]
        )

        elRect.attr({
          cx: x1,
          cy: y1,
          index: i,
          i,
          j,
          width: x2 - x1,
          height: y2 - y1,
          fill: pathFill,
        })

        elRect.node.classList.add('apexcharts-treemap-rect')

        this.helpers.addListeners(elRect)

        let fromRect = {
          x: x1 + (x2 - x1) / 2,
          y: y1 + (y2 - y1) / 2,
          width: 0,
          height: 0,
        }
        let toRect = {
          x: x1,
          y: y1,
          width: x2 - x1,
          height: y2 - y1,
        }

        if (w.config.chart.animations.enabled && !w.globals.dataChanged) {
          let speed = 1
          if (!w.globals.resized) {
            speed = w.config.chart.animations.speed
          }
          this.animateTreemap(elRect, fromRect, toRect, speed)
        }
        if (w.globals.dataChanged) {
          let speed = 1
          if (this.dynamicAnim.enabled && w.globals.shouldAnimate) {
            speed = this.dynamicAnim.speed

            if (
              w.globals.previousPaths[i] &&
              w.globals.previousPaths[i][j] &&
              w.globals.previousPaths[i][j].rect
            ) {
              fromRect = w.globals.previousPaths[i][j].rect
            }

            this.animateTreemap(elRect, fromRect, toRect, speed)
          }
        }

        let fontSize = this.getFontSize(r)

        let formattedText = w.config.dataLabels.formatter(this.labels[i][j], {
          value: w.globals.series[i][j],
          seriesIndex: i,
          dataPointIndex: j,
          w,
        })
        if (w.config.plotOptions.treemap.dataLabels.format === 'truncate') {
          fontSize = parseInt(w.config.dataLabels.style.fontSize, 10)
          formattedText = this.truncateLabels(
            formattedText,
            fontSize,
            x1,
            y1,
            x2,
            y2
          )
        }
        let dataLabels = null
        if (w.globals.series[i][j]) {
          dataLabels = this.helpers.calculateDataLabels({
            text: formattedText,
            x: (x1 + x2) / 2,
            y: (y1 + y2) / 2 + this.strokeWidth / 2 + fontSize / 3,
            i,
            j,
            colorProps,
            fontSize,
            series,
          })
        }
        if (w.config.dataLabels.enabled && dataLabels) {
          this.rotateToFitLabel(
            dataLabels,
            fontSize,
            formattedText,
            x1,
            y1,
            x2,
            y2
          )
        }
        elSeries.add(elRect)
        if (dataLabels !== null) {
          elSeries.add(dataLabels)
        }
      })

      const seriesTitle = w.config.plotOptions.treemap.seriesTitle
      if (w.config.series.length > 1 && seriesTitle && seriesTitle.show) {
        const sName = w.config.series[i].name || ''

        if (sName && bounds.xMin < Infinity && bounds.yMin < Infinity) {
          const {
            offsetX,
            offsetY,
            borderColor,
            borderWidth,
            borderRadius,
            style,
          } = seriesTitle

          const textColor = style.color || w.config.chart.foreColor
          const padding = {
            left: style.padding.left,
            right: style.padding.right,
            top: style.padding.top,
            bottom: style.padding.bottom,
          }

          const textSize = graphics.getTextRects(
            sName,
            style.fontSize,
            style.fontFamily
          )
          const labelRectWidth = textSize.width + padding.left + padding.right
          const labelRectHeight = textSize.height + padding.top + padding.bottom

          // Position
          const labelX = bounds.xMin + (offsetX || 0)
          const labelY = bounds.yMin + (offsetY || 0)

          // Draw background rect
          const elLabelRect = graphics.drawRect(
            labelX,
            labelY,
            labelRectWidth,
            labelRectHeight,
            borderRadius,
            style.background,
            1,
            borderWidth,
            borderColor
          )

          const elLabelText = graphics.drawText({
            x: labelX + padding.left,
            y: labelY + padding.top + textSize.height * 0.75,
            text: sName,
            fontSize: style.fontSize,
            fontFamily: style.fontFamily,
            fontWeight: style.fontWeight,
            foreColor: textColor,
            cssClass: style.cssClass || '',
          })

          elSeries.add(elLabelRect)
          elSeries.add(elLabelText)
        }
      }

      elSeries.add(elDataLabelWrap)
      ret.add(elSeries)
    })

    return ret
  }

  // This calculates a font-size based upon
  // average label length and the size of the box
  getFontSize(coordinates) {
    const w = this.w

    // total length of labels (i.e [["Italy"],["Spain", "Greece"]] -> 16)
    function totalLabelLength(arr) {
      let i,
        total = 0
      if (Array.isArray(arr[0])) {
        for (i = 0; i < arr.length; i++) {
          total += totalLabelLength(arr[i])
        }
      } else {
        for (i = 0; i < arr.length; i++) {
          total += arr[i].length
        }
      }
      return total
    }

    // count of labels (i.e [["Italy"],["Spain", "Greece"]] -> 3)
    function countLabels(arr) {
      let i,
        total = 0
      if (Array.isArray(arr[0])) {
        for (i = 0; i < arr.length; i++) {
          total += countLabels(arr[i])
        }
      } else {
        for (i = 0; i < arr.length; i++) {
          total += 1
        }
      }
      return total
    }

    let averagelabelsize =
      totalLabelLength(this.labels) / countLabels(this.labels)

    function fontSize(width, height) {
      let area = width * height
      let arearoot = Math.pow(area, 0.5)
      return Math.min(
        arearoot / averagelabelsize,
        parseInt(w.config.dataLabels.style.fontSize, 10)
      )
    }

    return fontSize(
      coordinates[2] - coordinates[0],
      coordinates[3] - coordinates[1]
    )
  }

  rotateToFitLabel(elText, fontSize, text, x1, y1, x2, y2) {
    const graphics = new Graphics(this.ctx)
    const textRect = graphics.getTextRects(text, fontSize)

    // if the label fits better sideways then rotate it
    if (
      textRect.width + this.w.config.stroke.width + 5 > x2 - x1 &&
      textRect.width <= y2 - y1
    ) {
      let labelRotatingCenter = graphics.rotateAroundCenter(elText.node)

      elText.node.setAttribute(
        'transform',
        `rotate(-90 ${labelRotatingCenter.x} ${
          labelRotatingCenter.y
        }) translate(${textRect.height / 3})`
      )
    }
  }

  // This is an alternative label formatting method that uses a
  // consistent font size, and trims the edge of long labels
  truncateLabels(text, fontSize, x1, y1, x2, y2) {
    const graphics = new Graphics(this.ctx)
    const textRect = graphics.getTextRects(text, fontSize)

    // Determine max width based on ideal orientation of text
    const labelMaxWidth =
      textRect.width + this.w.config.stroke.width + 5 > x2 - x1 &&
      y2 - y1 > x2 - x1
        ? y2 - y1
        : x2 - x1
    const truncatedText = graphics.getTextBasedOnMaxWidth({
      text: text,
      maxWidth: labelMaxWidth,
      fontSize: fontSize,
    })

    // Return empty label when text has been trimmed for very small rects
    if (text.length !== truncatedText.length && labelMaxWidth / fontSize < 5) {
      return ''
    } else {
      return truncatedText
    }
  }

  animateTreemap(el, fromRect, toRect, speed) {
    const animations = new Animations(this.ctx)
    animations.animateRect(el, fromRect, toRect, speed, () => {
      animations.animationCompleted(el)
    })
  }
}