Charts/Title.js

"use strict";

import { nest as d3Nest } from "d3-collection";

import defaultValue from "terriajs-cesium/Source/Core/defaultValue";
import defined from "terriajs-cesium/Source/Core/defined";

const defaultClassName = "base-chart-title";
const defaultHeight = 30; // The additional height of the title, which may in fact be the legend.

/**
 * Handles the drawing of the chart title, which may be a string or a legend.
 *
 * @param  {String} [titleSettings.type='string'] May be 'string' or 'legend'.
 * @param  {String} [titleSettings.title] For 'string'-type titles, the title.
 * @param  {String} [titleSettings.className] The className to use for the title DOM element. Defaults to 'base-chart-title'.
 * @param  {Number} [titleSettings.height=defaultTitleHeight] The height of the title bar.
 */
const Title = {
  className(titleSettings) {
    return titleSettings && titleSettings.className
      ? titleSettings.className
      : defaultClassName;
  },

  getHeight(titleSettings) {
    return defaultValue(
      defined(titleSettings) ? titleSettings.height : 0,
      defaultHeight
    );
  },

  create(d3Element, titleSettings) {
    // For a nicely centered title, use css:  .chart-title {left: 0, right: 0, text-align: center;} and maybe {margin: 0 auto;}.
    d3Element
      .append("div")
      .attr("class", Title.className(titleSettings))
      .style("opacity", 1e-6)
      .style("position", "absolute");
  },

  enterUpdateAndExit(
    d3Element,
    titleSettings,
    margin,
    state,
    transitionDuration
  ) {
    // The title might be the legend, or a simple string.
    const title = d3Element
      .select("." + Title.className(titleSettings))
      .style("top", margin.top + "px");
    title
      .transition()
      .duration(transitionDuration)
      .style("opacity", Title.getHeight(titleSettings) > 0 ? 1 : 1e-6);
    if (defined(titleSettings)) {
      let titleData = state.data;
      if (titleSettings.type === "string") {
        titleData = [{ id: "_string__", name: titleSettings.title }];
      }

      // in d3 v4,  selection.data method returns new selections
      // rather than modifying the selection in-place.
      const titleComponents = title
        .selectAll(".title-component")
        .data(titleData, d => d.id);
      // Check whether there are multiple category names and/or column names.
      const numberOfCategories = d3Nest().key(d => d.categoryName).length;
      const numberOfColumnNames = d3Nest().key(d => d.name).length;
      // This is to only show the interesting parts of the name & categoryName in the title,
      // similar to Tooltip.js.
      const getName = function(d, index) {
        if (!d.categoryName) {
          return d.name;
        }
        if (numberOfColumnNames === 1) {
          return d.categoryName + (index === 0 ? " " + d.name : "");
        }
        if (numberOfCategories === 1) {
          return (index === 0 ? d.categoryName + " " : "") + d.name;
        }
        if (d.name === d.categoryName) {
          return d.categoryName;
        }
        return d.categoryName + " " + d.name;
      };
      // Enter.
      const addedTitleComponents = titleComponents
        .enter()
        .append("span")
        .attr("class", "title-component");

      if (titleSettings.type === "legend") {
        addedTitleComponents.append("span").attr("class", "color");
      }
      addedTitleComponents.append("span").attr("class", "name");
      // Enter and update.
      const mergedTitleComponents = addedTitleComponents.merge(titleComponents);
      mergedTitleComponents
        .select(".color")
        .style("background-color", d => d.color)
        .style("border-radius", d => {
          return d.type === "momentPoints" ? "50%" : "0";
        });
      mergedTitleComponents.select(".name").text(getName);
      // Exit.
      titleComponents.exit().remove();
    }
  }
};

module.exports = Title;