Source: htmltable.js

import { isgeojson } from "./helpers/helpers.js";

/**
 * @function htmltable
 * @summary  View a data table.
 * @param {object|array} data - a GeoJSON FeatureCollection or an array of features
 * @param {object} options - Optional parameters
 * @param {number} [options.maxrows] - Number max of lines
 * @returns {HTMLElement} - A html sortable table.
 * @example
 * geottolbox.htmltable(*a geojson*)
 */

export function htmltable(data, { maxrows = null } = {}) {
  let x;
  let test = isgeojson(data);
  if (test) {
    x = data.features.map((d) => {
      return Object.assign(
        {
          ["GEOMETRY"]: d?.geometry?.type,
          // +
          // " (" +
          // d.geometry.coordinates.flat(10).length / 2 +
          // " pts)"
        },
        d.properties
      );
    });
  } else {
    x = data;
  }

  let table = document.createElement("table");
  let thead = document.createElement("thead");
  let headerRow = document.createElement("tr");
  const headers = Object.keys(x[0]);

  headers.forEach((header, index) => {
    const values = x.map((d) => d[header]);
    const count = values.length;
    console.log(values);
    const nb = values.filter(
      (d) =>
        !["", " ", "  ", undefined, NaN, null, Infinity, -Infinity].includes(d)
    ).length;

    let th = document.createElement("th");
    th.innerHTML =
      header +
      `<span style='font-size: 0.7em; font-style: italic ; font-weight: normal; color: #808080'><br/>${nb}/${count}</span>`;
    th.style.cursor = "pointer";
    th.style.backgroundColor = "white";
    th.style.color = "#444";
    th.style.padding = "10px";
    th.style.textAlign = "left";
    th.style.position = "sticky";
    th.style.top = "0";
    th.style.zIndex = "1";
    th.style.borderBottom = "2px solid #444";
    th.style.fontSize = "1em";

    let asc = true;
    th.addEventListener("click", () => {
      asc = !asc;
      sortTable(index, asc);
    });

    headerRow.appendChild(th);
  });

  thead.appendChild(headerRow);
  table.appendChild(thead);

  let tbody = document.createElement("tbody");
  const displayedData = maxrows ? x.slice(0, maxrows) : x;

  function renderTable(data) {
    tbody.innerHTML = "";
    data.forEach((row, index) => {
      let tr = document.createElement("tr");
      tr.style.backgroundColor = index % 2 === 0 ? "#f9f9f9" : "#fff";
      tr.style.fontSize = "0.9em";
      headers.forEach((header, i) => {
        let td = document.createElement("td");
        td.textContent = row[header];
        td.style.padding = "5px";
        td.style.borderBottom = "1px solid #ddd";
        td.style.textAlign = "left";
        if (test) {
          td.style.backgroundColor = i === 0 ? "#444" : undefined;
          td.style.color = i === 0 ? "white" : undefined;
        }
        tr.appendChild(td);
      });
      tbody.appendChild(tr);
    });
  }

  function sortTable(columnIndex, ascending) {
    displayedData.sort((a, b) => {
      let valA = a[headers[columnIndex]];
      let valB = b[headers[columnIndex]];
      if (!isNaN(parseFloat(valA)) && !isNaN(parseFloat(valB))) {
        return ascending ? valA - valB : valB - valA;
      }
      return ascending ? valA.localeCompare(valB) : valB.localeCompare(valA);
    });
    renderTable(displayedData);
  }

  renderTable(displayedData);
  table.appendChild(tbody);

  let container = document.createElement("div");
  container.style.overflowY = "auto";
  container.style.maxHeight = "400px";
  container.style.margin = "0";
  container.style.padding = "0";
  container.style.boxSizing = "border-box";
  container.style.overflowX = "hidden";

  container.appendChild(table);
  setTimeout(() => {
    const tableWidth = table.offsetWidth + 20;
    const containerWidth = container.offsetWidth;
    if (tableWidth > containerWidth) {
      container.style.overflowX = "scroll";
    } else {
      container.style.width = `${tableWidth}px`;
      container.style.overflowX = "hidden";
    }
  }, 0);

  return container;
}