JSFiddle - React, Tailwind, and code Playground
TypeScript box selector. Select a group of boxes
by Glen Cheney
HTML
<ul id="boxes">
<li class="box"></li>
<li class="box"></li>
<li class="box"></li>
<li class="box"></li>
<li class="box"></li>
<li class="box"></li>
<li class="box"></li>
<li class="box"></li>
<li class="box"></li>
<li class="box"></li>
<li class="box"></li>
<li class="box"></li>
<li class="box"></li>
<li class="box"></li>
</ul>
<div class="selection"></div>
CSS
body {
font-family: sans-serif;
box-sizing: border-box;
padding-bottom: 400px;
}
*,
*::before,
*::after {
box-sizing: inherit;
}
#boxes {
display: grid;
grid-gap: 6px;
padding: 0;
margin: 0;
list-style: none;
grid-template-columns: repeat(auto-fill, 100px);
}
.box {
width: 100px;
height: 100px;
background-color: white;
border: 3px solid navy;
}
.box--selected {
background-color: skyblue;
}
.selection {
position: absolute;
top: 0;
left: 0;
display: none;
width: 0;
height: 0;
background-color: rgba(0, 0, 0, 0.5);
border: 2px solid navy;
}
TypeScript
// https://imgur.com/a/eLeU2
// Save positions of all boxes. Update on resize.
// Listen for mousedown on body. Save starting position.
// On mouse move, test every box to see if it overlaps with the selection.
// On mouse up, hide the selection.
class Rect {
constructor(x: number, y: number, w: number, h: number) {
this.left = x;
this.top = y;
this.width = w;
this.height = h;
}
get right(): number {
return this.left + this.width;
}
get bottom(): number {
return this.top + this.height;
}
static intersects(a: Rect, b: Rect): boolean {
return (
a.left <= b.right &&
b.left <= a.right &&
a.top <= b.bottom &&
b.top <= a.bottom
);
}
}
class Box {
constructor(public element: HTMLElement, public rect: Rect) {}
}
class Point {
constructor(public x: number, public y: number) {}
}
class Selector {
private selection: Rect;
private start: Point;
constructor() {
this.boxes = this.getBoxes();
this._selectionElement = document.querySelector('.selection');
this._handleResize = this._handleResize.bind(this);
this._handleMouseDown = this._handleMouseDown.bind(this);
this._handleMouseMove = this._handleMouseMove.bind(this);
this._handleMouseUp = this._handleMouseUp.bind(this);
window.addEventListener('resize', this._handleResize);
document.body.addEventListener('mousedown', this._handleMouseDown);
}
private getBoxes(): Box[] {
return Array.from(
document.querySelectorAll('.box'),
this.getSingleBox,
);
}
private getSingleBox(element: HTMLElement): Box {
const { top, left, width, height } = element.getBoundingClientRect();
const rect = new Rect(left, top, width, height);
return new Box(element, rect);
}
private deselectAll(): void {
this.boxes.forEach((box: Box) => {
box.element.classList.remove('box--selected');
});
}
private drawSelection(): void {
// Set the size of the overlay.
...