component/htmlTable.js

import * as d3 from "d3";
import dataTransform from "../dataTransform";

/**
 * Simple HTML Table
 *
 * @module
 */
export default function() {

	/* HTML List Element */
	let tableEl;

	/* Default Properties */
	let classed = "htmlTable";
	let width = 800;

	// Dispatch (Custom events)
	let dispatch = d3.dispatch("customValueMouseOver", "customValueMouseOut", "customValueClick", "customSeriesMouseOver", "customSeriesMouseOut", "customSeriesClick");

	/**
	 * Constructor
	 *
	 * @constructor
	 * @alias htmlTable
	 * @param {d3.selection} selection - The chart holder D3 selection.
	 */
	function my(selection) {
		selection.each(function(data) {
			const { rowKeys, columnKeys } = dataTransform(data).summary();

			// Create HTML Table 'table' element (if it does not exist already)
			if (!tableEl) {
				tableEl = d3.select(this)
					.append("table")
					.classed("d3ez", true)
					.classed(classed, true)
					.attr("width", width);
			} else {
				tableEl.selectAll("*")
					.remove();
			}
			const head = tableEl.append("thead");
			const foot = tableEl.append("tfoot");
			const body = tableEl.append("tbody");

			// Add table headings
			const hdr = head.append("tr");

			hdr.selectAll("th")
				.data(function() {
					// Tack on a blank cell at the beginning,
					// this is for the top of the first column.
					return [""].concat(columnKeys);
				})
				.enter()
				.append("th")
				.html(function(d) {
					return d;
				});

			// Add table body
			const rowsSelect = body.selectAll("tr")
				.data(data);

			const rows = rowsSelect.enter()
				.append("tr")
				.attr("class", function(d) {
					return d.key;
				})
				.on("click", function(d) { dispatch.call("customSeriesClick", this, d); })
				.merge(rowsSelect);

			// Add the first column of headings (categories)
			rows.append("th")
				.html(function(d) {
					return d.key;
				});

			// Add the main data values
			rows.selectAll("td")
				.data(function(d) {
					return d.values;
				})
				.enter()
				.append("td")
				.attr("class", function(d) {
					return d.key;
				})
				.html(function(d) {
					return d.value;
				})
				.on("mouseover", function(d) { dispatch.call("customValueMouseOver", this, d); });
		});
	}

	/**
	 * Width Getter / Setter
	 *
	 * @param {number} _v - Width in px.
	 * @returns {*}
	 */
	my.width = function(_v) {
		if (!arguments.length) return width;
		width = _v;
		return this;
	};

	/**
	 * Class Getter / Setter
	 *
	 * @param {string} _v - HTML class.
	 * @returns {*}
	 */
	my.classed = function(_v) {
		if (!arguments.length) return classed;
		classed = _v;
		return this;
	};

	/**
	 * Dispatch On Getter
	 *
	 * @returns {*}
	 */
	my.on = function() {
		let value = dispatch.on.apply(dispatch, arguments);
		return value === dispatch ? my : value;
	};

	return my;
}