RenJith
RenJith

Reputation: 557

Custom styling for the d3 treemap tooltip

I am working with the d3.js treemap layout . Find my code link here https://codesandbox.io/s/loving-mccarthy-wfbtg. The tooltip consists of the hierarchy like flare/vis 436777 but I need only the Label and the total count or value like vis 436777 . Current tooltip enter image description here

Finally added custom tooltip but the tooltip sticks on the canvas on click of a segment rect. Added th screenshot enter image description here

Upvotes: 0

Views: 882

Answers (1)

Jasdeep Singh
Jasdeep Singh

Reputation: 8321

I checked and found out that you have written a function called "name" where you are finding ancestors and joining them. Please found below my finding and code you need to write to fulfill your requirement. Add the CSS to styles.css and replace your class Treegraph with the below class.

class Treegraph extends React.Component {
  state = {
    width: 400,
    height: 400
  };
  createTreeChart = () => {
    const width = 550;
    const height = 500;
    const padding = 60;
    const format = d3.format(",d");
    const name = d =>
      d
      .ancestors()
      .reverse()
      .map(d => d.data.segment)
      .join(" / ");
    // const name = d => d.data.segment;
    function tile(node, x0, y0, x1, y1) {
      d3.treemapBinary(node, 0, 0, width, height);
      for (const child of node.children) {
        child.x0 = x0 + (child.x0 / width) * (x1 - x0);
        child.x1 = x0 + (child.x1 / width) * (x1 - x0);
        child.y0 = y0 + (child.y0 / height) * (y1 - y0);
        child.y1 = y0 + (child.y1 / height) * (y1 - y0);
      }
    }
    const treemap = data =>
      d3.treemap().tile(tile)(
        d3
        .hierarchy(data)
        .sum(d => d.value)
        .sort((a, b) => b.value - a.value)
      );
    const svg = d3
      .select("#chart")
      .append("svg")
      .attr("viewBox", [0.5, -30.5, width, height + 30])
      .style("font", "16px sans-serif");

    const x = d3.scaleLinear().rangeRound([0, width]);
    const y = d3.scaleLinear().rangeRound([0, height]);

    // const svg = d3
    //   .create("svg")
    //   .select("#chart")
    //   .append("svg")
    //   .attr("viewBox", [0.5, -30.5, width, height + 30])
    //   .style("font", "10px sans-serif");

    let group = svg.append("g").call(render, treemap(data));

    function render(group, root) {
      const node = group
        .selectAll("g")
        .data(root.children.concat(root))
        .join("g");

      node
        .filter(d => (d === root ? d.parent : d.children))
        .attr("cursor", "pointer")
        .on("click", d => (d === root ? zoomout(root) : zoomin(d)));

      //node.append("title").text(d => `${name(d)}\n(${format(d.data.count)})`);
      var tool = d3
        .select("body")
        .append("div")
        .attr("class", "toolTip");

      d3.select(self.frameElement).style("height", height + 300 + "px");
      d3.select(self.frameElement).style("width", width + 20 + "px");

      node
        .append("rect")
        .attr("id", d => (d.leafUid = "leaf"))
        .attr("fill", d =>
          d === root ? "#fff" : d.children ? "#045c79" : "#045c79"
        )
        .attr("stroke", "#fff")
        .on("mousemove", function(d) {
          tool.style("left", d3.event.pageX + 10 + "px");
          tool.style("top", d3.event.pageY - 20 + "px");
          tool.style("display", "inline-block");
          tool.html(`${d.data.segment}<br />(${format(d.data.count)})`);
        })
        .on("mouseout", function(d) {
          tool.style("display", "none");
        });

      node
        .append("clipPath")
        .attr("id", d => (d.clipUid = "clip"))
        .append("use")
        .attr("xlink:href", d => d.leafUid.href);

      node
        .append("text")
        .attr("clip-path", d => d.clipUid)
        .attr("font-weight", d => (d === root ? "bold" : null))
        .attr("font-size", d => {
          if (d === root) return "0.8em";
          const width = x(d.x1) - x(d.x0),
            height = y(d.y1) - y(d.y0);
          return Math.max(
            Math.min(
              width / 5,
              height / 2,
              Math.sqrt(width * width + height * height) / 25
            ),
            9
          );
        })
        .attr("text-anchor", d => (d === root ? null : "middle"))
        .attr("transform", d =>
          d === root ?
          null :
          `translate(${(x(d.x1) - x(d.x0)) / 2}, ${(y(d.y1) - y(d.y0)) /
                2})`
        )
        .selectAll("tspan")
        .data(d =>
          d === root ?
          name(d).split(/(?=\/)/g) :
          d.value < 2 ?
          `${d.data.segment.substring(0, 3)}...`.split(/(\s+)/) :
          d.data.segment.split(/(\s+)/).concat(format(d.data.count))
        )
        .join("tspan")
        .attr("x", 3)
        .attr(
          "y",
          (d, i, nodes) =>
          `${(i === nodes.length - 1) * 0.3 + (i - nodes.length / 2) * 0.9}em`
        )
        // .attr("fill-opacity", (d, i, nodes) =>
        //   i === nodes.length - 1 ? 0.7 : null
        // )
        // .attr("font-weight", (d, i, nodes) =>
        //   i === nodes.length - 1 ? "normal" : null
        // )
        .text(d => d);
      node
        .selectAll("text")
        .classed("text-title", d => d === root)
        .classed("text-tile", d => d !== root)
        .filter(d => d === root)
        .selectAll("tspan")
        .attr("y", "1.1em")
        .attr("x", undefined);
      group.call(position, root);
    }

    function position(group, root) {
      group
        .selectAll("g")
        .attr("transform", d =>
          d === root ? `translate(0,-30)` : `translate(${x(d.x0)},${y(d.y0)})`
        )
        .select("rect")
        .attr("width", d => (d === root ? width : x(d.x1) - x(d.x0)))
        .attr("height", d => (d === root ? 30 : y(d.y1) - y(d.y0)));
    }

    // When zooming in, draw the new nodes on top, and fade them in.
    function zoomin(d) {
      x.domain([d.x0, d.x1]);
      y.domain([d.y0, d.y1]);
      const group0 = group.attr("pointer-events", "none");
      const group1 = (group = svg.append("g").call(render, d));
      svg
        .transition()
        .duration(750)
        .call(t =>
          group0
          .transition(t)
          .remove()
          .call(position, d.parent)
        )
        .call(t =>
          group1
          .transition(t)
          .attrTween("opacity", () => d3.interpolate(0, 1))
          .call(position, d)
        );
    }

    // When zooming out, draw the old nodes on top, and fade them out.
    function zoomout(d) {
      x.domain([d.parent.x0, d.parent.x1]);
      y.domain([d.parent.y0, d.parent.y1]);
      const group0 = group.attr("pointer-events", "none");
      const group1 = (group = svg.insert("g", "*").call(render, d.parent));
      svg
        .transition()
        .duration(750)
        .call(t =>
          group0
          .transition(t)
          .remove()
          .attrTween("opacity", () => d3.interpolate(1, 0))
          .call(position, d)
        )
        .call(t => group1.transition(t).call(position, d.parent));
    }

    return svg.node();
  };
  componentDidMount() {
    this.createTreeChart();
  }
  render() {
    return ( <
      React.Fragment >
      <
      div id = "chart" / >
      <
      /React.Fragment>
    );
  }
}
.toolTip {
  position: absolute;
  display: none;
  width: auto;
  height: auto;
  background: none repeat scroll 0 0 white;
  border: 0 none;
  border-radius: 8px 8px 8px 8px;
  box-shadow: -3px 3px 15px #888888;
  color: black;
  font: 12px sans-serif;
  padding: 5px;
  text-align: center;
}

Upvotes: 1

Related Questions