Home Reference Source

src/svg/components/sunburstDisk.js

import {partition, stratify, arc} from 'd3';


export class SunburstDisk {
  constructor(xRadialAxis, yRadialAxis) {
    this.x = xRadialAxis;
    this.y = yRadialAxis;
    this.arcGen = arc()
      .startAngle((d) => Math.max(0, Math.min(2 * Math.PI, this.x(d.x0))))
      .endAngle((d) => Math.max(0, Math.min(2 * Math.PI, this.x(d.x1))))
      .innerRadius((d) => Math.max(0, this.y(d.y0)))
      .outerRadius((d) => Math.max(0, this.y(d.y1)));
  }

  update(svg, config, data) {

    // Remove all the paths before redrawing
    this._removePaths(svg);

    // Create layout partition
    let root = stratify()
      .id((d) => d.id)
      .parentId((d) => d.parent)
      (data);

    root.sum((d) =>  d.value);
    partition()(root);

    // Draw the paths (arcs)
    let paths = svg.selectAll('path')
      .data(root.descendants())
      .enter().append('path')
      .attr('d', this.arcGen)
      .style('fill', (d) => {
        if (!d.parent) {
          return 'white';
        } else {
          return config.colorScale(d.data.label);
        }
      })
      .style('stroke', '#fff')
      .style('stroke-width', '2')
      .style('shape-rendering', 'crispEdge');

      paths // TODO extract events to config?
        .on('mouseover.default', (d) => {
          let ancestors = this._getAncestors(d);
          // Fade all the arcs
          if (ancestors.length > 0) {
            svg.selectAll('path')
                .style('opacity', 0.3);
          }
          svg.selectAll('path')
            .filter((node) => ancestors.indexOf(node) >= 0)
            .style('opacity', 1);
          // Hightlight the hovered arc
            svg.select('.text-indicator .label').text(d.data.label);
            svg.select('.text-indicator .value').text(d.value);
        })
        .on('mouseout.default', (d) => {
          svg.selectAll('path').style('opacity', 1);
          svg.select('.text-indicator .label').style('font-weight', 'normal');
          svg.select('.text-indicator .label').text('');
          svg.select('.text-indicator .value').text('');
        })
      ;

    paths
      .on('mousedown.user', config.onDown)
      .on('mouseup.user', config.onUp)
      .on('mouseleave.user', config.onLeave)
      .on('mouseover.user', config.onHover)
      .on('click.user', config.onClick);

    // ???
    svg.select(self.frameElement).style('height', this.height + 'px');
  }

  /**
   * Removes all the paths (arcs). Doing this before each redraw prevents the
   * transition to mess up the arcs.
   * @private
   */
  _removePaths(svg) {
    svg.selectAll('path').remove();
  }

  /**
   * From: http://bl.ocks.org/kerryrodden/7090426
   * @param node
   * @returns {Array}
   * @private
   */
  _getAncestors(node) {
    let path = [];
    let current = node;
    while (current.parent) {
      path.unshift(current);
      current = current.parent;
    }
    return path;
  }

  render(svg, config) {
    //Do nothing, since disk render only when new data is received.
  }
}