JSFiddle - React, Tailwind, and code Playground

by Malleys

HTML

<button onclick="document.querySelector('table').merge();">Merge</button>
<!-- <button onclick="document.querySelector('table').split();">Split</button> -->

<div id="tableEditor"></div>

CSS

table {
	height: 500px;
	width: 500px;
	border-collapse: collapse;
	position: relative;
	user-select: none;
	cursor: default;
	table-layout: fixed;
	font: 1em Helvetica Neue, Segoe UI, Roboto, Ubuntu, sans-serif;
}

td {
	border: 1px gray solid;
}

td:focus {
	box-shadow: inset 0 0 0 2px rgba(111, 168, 220, 0.66);
	outline: 0;
}

JavaScript

class Box {
	constructor(x = 0, y = 0, width = 0, height = 0) {
		this.x = x;
		this.y = y;
		this.width = width;
		this.height = height;
	}

	contains({ x, y, width, height }) {				
		return (
			x > this.x &&
			y > this.y &&
			(x + width) < (this.x + this.width) &&
			(y + height) < (this.y + this.height)
		);
	}

	overlaps({ x, y, width, height }) {
		return (
			1.0001 * Math.max(x, this.x) < 0.9998 * Math.min(x + width, this.x + this.width) &&
			1.0001 * Math.max(y, this.y) < 0.9998 * Math.min(y + height, this.y + this.height)
		);
	}

	equals({ x, y, width, height }) {
		return (
			x === this.x &&
			y === this.y &&
			width === this.width &&
			height === this.height
		);
	}

	add({ x, y, width, height }) {
		return new Box(
			Math.min(x, this.x),
			Math.min(y, this.y),
			Math.max(x + width, this.x + this.width) - Math.min(x, this.x),
			Math.max(y + height, this.y + this.height) - Math.min(y, this.y)
		);
	}

	intersection({ x, y, width, height }) {
		return new Box(
			Math.max(x, this.x),
			Math.max(y, this.y),
			Math.min(x + width, this.x + this.width) - Math.max(x, this.x),
			Math.min(y + height, this.y + this.height) - Math.max(y, this.y)
		);
	}

	relativeTo({ x, y }) {
		return new Box(
			this.x - x,
			this.y - y,
			this.width,
			this.height
		);
	}

	relativeToElement(element) {
		return this.relativeTo(element.getBoundingClientRect());
	}

	static from({ x = 0, y = 0, width = 0, height = 0 } = {}) {
		return new Box(x, y, width, height);
	}
}

class EditableTable extends HTMLTableElement {
	constructor() {
		super();

		const document = this.ownerDocument;
		this.selectionElement = document.createElement("div");
		this.selectionElement2 = document.createElement("div");
		this.selectionElement.style.cssText = `
			position: absolute;
			background: rgba(111, 168, 220, 0.66);
			pointer-events: none;
		`;
		this.selectionElement2.style.cssText = `
			position: absolute;
			outline: 1px dashed rgba(0, 0, 0, 0.5);
			pointer-events:...