JSFiddle - React, Tailwind, and code Playground

by Josh Pullen

CSS

table, th, td {
  border: 1px solid black;
}

JavaScript

import { html, render, useState } from "https://unpkg.com/htm/preact/standalone.module.js";

function App() {
	const [tables, setTables] = useState([
  	{
    	id: "0",
    	name: { singular: "person", plural: "people" },
      columns: [
      	{ id: "0", name: "name", type: "string" },
      ],
    },
  ]);
  
  const [rows, setRows] = useState([
  	{
    	id: "0",
      table: "0",
      entries: {
        "0": { value: "Josh Pullen" },
      },
    }
  ]);
  
  function getTable(id) {
    return tables.find(table => table.id === id);
  }
  
  function setTable(id, newTable) {
  	setTables(tables.map(table => {
    	if (table.id === id) return newTable;
      return table;
    }));
  }
  
  function addColumn(tableId) {
  	const table = getTable(tableId);
    setTable(tableId, {
      ...table,
      columns: [
        ...table.columns,
        {
          id: Math.random().toString(16).slice(2),
          name: "",
          type: "string"
        }
      ]
    });
  }
  
  function getRow(id) {
    return rows.find(row => row.id === id);
  }
  
  function setRow(id, newRow) {
  	setRows(rows.map(row => {
    	if (row.id === id) return newRow;
      return row;
    }));
  }
  
  function setRowEntry(rowId, columnId, newEntry) {
  	const row = getRow(rowId);
  	setRow(rowId, {
    	...row,
      entries: {
      	...row.entries,
        [columnId]: newEntry
      }
    });
  }

	return html`
  	<div>
    	<h1>Tables</h1>
    	${tables.map(table => html`
      	<div>
        	<h2>${table.name.plural}</h2>
          <table >
          	<thead>
            	<tr>
              	${table.columns.map(column => html`
                	<th>${column.name}</th>
                `)}

                <th>
                	<button
                    onClick=${() => {
											addColumn(table.id);
                    }}
                  >
                  	+
                  </button>
                </th>
              </tr>
            </thead>
            <tbody>
    ...