Source: vga.barchart.js

/**
 * Clase que representa una gráfica
 * @extends ControlPrimitive
 */
class BarChart extends ControlPrimitive {
  #chart;
  #data;
  #initialized = false;

  /**
   * Crea una nueva gráfica
   * @param {string} name Nombre de la gráfica
   * @param {string} label Etiqueta que se mostrará
   * @param {string} description Descripción que se mostrará
   * @constructor
   */
  constructor(name, label, description) {
    super(name, label, description);
  }

  /**
   * Retorna el html asociado a este control
   * @returns Código html del control
   */
  getHtmlCode() {
    return '<div id="' + this.getName() + 'Div" class="chartDiv"></div>';
  }

  /**
   * Inicia la gráfica
   * @param {Array} dataset Array de datos
   */
  initChart(dataset) {
    var w = $("#" + this.getName() + "Div").width();
    var h = $("#" + this.getName() + "Div").height();

    this.#data = dataset;

    var xScale = d3
      .scaleBand()
      .domain(d3.range(this.#data.length))
      .rangeRound([0, w])
      .paddingInner(0.05);

    var yScale = d3
      .scaleLinear()
      .domain([0, d3.max(this.#data)])
      .range([0, h]);

    this.#chart = d3
      .select("#" + this.getName() + "Div")
      .append("svg")
      .attr("width", w)
      .attr("height", h);
    var j = 0;
    this.#chart
      .selectAll("rect")
      .data(this.#data)
      .enter()
      .append("rect")
      .attr("x", function (d, i) {
        return xScale(i);
      })
      .attr("y", function (d) {
        return h - yScale(d);
      })
      .attr("width", xScale.bandwidth())
      .attr("height", function (d) {
        return yScale(d);
      })
      .attr("fill", function (d) {
        return "rgb(0, 0, " + Math.floor(d * 30) + ")";
      })
      .attr("id", function () {
        return "chartBar" + j++;
      });

    //Create labels
    this.#chart
      .selectAll("text")
      .data(this.#data)
      .enter()
      .append("text")
      .text(function (d) {
        return d;
      })
      .attr("text-anchor", "middle")
      .attr("x", function (d, i) {
        return xScale(i) + xScale.bandwidth() / 2;
      })
      .attr("y", function (d) {
        return h - yScale(d) + 14;
      })
      .attr("font-family", "sans-serif")
      .attr("font-size", "11px")
      .attr("fill", "white");

    this.#initialized = true;
  }

  updateData() {
    this.#chart.remove();
    var w = $("#" + this.getName() + "Div").width();
    var h = $("#" + this.getName() + "Div").height();

    var xScale = d3
      .scaleBand()
      .domain(d3.range(this.#data.length))
      .rangeRound([0, w])
      .paddingInner(0.05);

    var yScale = d3
      .scaleLinear()
      .domain([0, d3.max(this.#data)])
      .range([0, h]);

    this.#chart = d3
      .select("#" + this.getName() + "Div")
      .append("svg")
      .attr("width", w)
      .attr("height", h);
    var j = 0;
    this.#chart
      .selectAll("rect")
      .data(this.#data)
      .enter()
      .append("rect")
      .attr("x", function (d, i) {
        return xScale(i);
      })
      .attr("y", function (d) {
        return h - yScale(d);
      })
      .attr("width", xScale.bandwidth())
      .attr("height", function (d) {
        return yScale(d);
      })
      .attr("fill", function (d) {
        return "rgb(0, 0, " + Math.floor(d * 30) + ")";
      })
      .attr("id", function () {
        return "chartBar" + j++;
      });

    //Create labels
    this.#chart
      .selectAll("text")
      .data(this.#data)
      .enter()
      .append("text")
      .text(function (d) {
        return d;
      })
      .attr("text-anchor", "middle")
      .attr("x", function (d, i) {
        return xScale(i) + xScale.bandwidth() / 2;
      })
      .attr("y", function (d) {
        return h - yScale(d) + 14;
      })
      .attr("font-family", "sans-serif")
      .attr("font-size", "11px")
      .attr("fill", "white");
  }

  /**
   * Establece los datos del gráfico
   * @param {object[]} data Datos del gráfico
   */
  setData(data) {
    this.#data = data;
    if (data != undefined) {
      if (this.#initialized) {
        this.updateData();
      } else {
        this.initChart();
      }
    }
  }

  /**
   * Obtiene los datos del gráfico
   * @returns Datos del gráfico
   */
  getData() {
    return this.#data;
  }

  /**
   * Selecciona una barra
   * @param {number} id Identificador de la barra
   * @param {string} color Nombre del color del borde. Rojo por defecto
   */
  selectBar(id, color = "red") {
    this.setBarStyle(id, "outline: solid " + color + ";");
  }

  /**
   * Setea el estilo de una barra
   * @param {number} id Identificador de la barra
   * @param {string} style Estilo que se quiere poner a la barra
   */
  setBarStyle(id, style) {
    if (Utilities.hasAttr("#chartBar" + id, "style")) {
      this.removeBarStyle(id);
    }
    $("#chartBar" + id).attr("style", style);
  }

  /**
   * Elimina el estilo de una barra
   * @param {number} id Identificador de la barra
   */
  removeBarStyle(id) {
    $("#chartBar" + id).removeAttr("style");
  }

  /**
   * Deselecciona una barra
   * @param {number} id Identificador de la barra
   */
  unselectBar(id) {
    this.removeBarStyle(id);
  }

  /**
   * Obtiene el estulo de una barra
   * @param {number} id Identificador de la barra
   * @returns El estilo de la barra en caso de existir
   */
  getBarStyle(id) {
    return $("#chartBar" + id).attr("style");
  }

  /**
   * Setea el valor de una barra
   * @param {number} id Identificador de la barra
   * @param {number} value Valor de la barra
   */
  setBarValue(id, value) {
    this.#data[id] = value;
    if (this.#initialized) {
      this.updateData();
    } else {
      this.initChart();
    }
  }

  /**
   * Vacia la gráfica
   */
  clearData() {
    this.#data = null;
    if (this.#initialized) {
      this.#chart.remove();
    }
  }
}