SelectionJS Template

by Simonwep

HTML

<ul>
  <li id="startFired"></li>
  <li id="moveFired"></li>
  <li id="stopFired"></li>
  <li id="changeFired"></li>
</ul>

<div class="container">
    <div class="box"></div>
</div>

CSS

.box {
    display: grid;
    grid-template-columns: repeat(28, 1fr);
    grid-gap: 0.4em;
}

.box > div {
    height: 3em;
    width: 3em;
    background: rgba(66, 68, 90, 0.075);
}

.box > div.selected {
    background: #7febc2;
}

.selection-area {
    background: rgba(0, 0, 255, 0.1);
    border-radius: 0.1em;
    border: 0.05em solid rgba(0, 0, 255, 0.2);
}

.container {
  width: 70vmin;
  overflow: auto;
  margin: 15vmin;
}

JavaScript

import SelectionArea from "https://cdn.jsdelivr.net/npm/@simonwep/selection-js/lib/selection.min.mjs"

// Create selectable elements
const container = document.querySelector('.box');
for(let i = 0; i < 500; i++) {
	const div = document.createElement('div');
  container.appendChild(div);
}

const doc = document.documentElement;
const handlePageScroll = ({clientY}) => {
		const {scrollTop, scrollHeight, clientHeight} = doc;
		if (clientY <= 0 && scrollTop){
  			doc.scrollBy(0, -10);
  	} else if (clientY >= clientHeight && (clientHeight + scrollTop) !== scrollHeight) {
  			doc.scrollBy(0, 10);
  	}
}

const stats = new Proxy({startFired: 0,	moveFired: 0, stopFired: 0, changeFired: 0}, {
	set(obj, prop, value) {
    obj[prop] = value;
    
    for (const [prop, value] of Object.entries(obj)) {
    	document.getElementById(prop).innerText = `${prop}: ${value}`;
    }
    
    return true;
  }
});

stats.startFired = 0;
const selection = new SelectionArea({

    // All elements in this container can be selected
    selectables: ['.box > div'],

    // The container is also the boundary in this case
    boundaries: ['.container'],
}).on('start', ({store, event}) => {
		stats.startFired++;

    // Remove class if the user isn't pressing the control key or ⌘ key
    if (!event.ctrlKey && !event.metaKey) {

        // Unselect all elements
        for (const el of store.stored) {
            el.classList.remove('selected');
        }

        // Clear previous selection
        selection.clearSelection();
    }

		doc.addEventListener('mousemove', handlePageScroll);
}).on('move', (() => {
	let lastAdded = [], lastRemoved = [];
  const isEqual = (a, b) => a.length === b.length && a.every(el => b.includes(el));

	return ({store: {changed: {added, removed}}}) => {
		stats.moveFired++;
    
    if (!isEqual(lastAdded, added) || !isEqual(lastRemoved, removed)) {
			stats.changeFired++;

    	// Add a custom class to the elements that where selected.
    	for (const el of...