<!DOCTYPE html>
<html lang="en">

	<head>
		<meta charset="utf-8">
		<title>Pick at Random</title>
		<meta name="viewport" content="width=device-width, initial-scale=1.0">

		<style type="text/css">
			body {
				margin: 1em auto;
				max-width: 40em;
				width: 88%;
			}

			pick-at-random [role="status"]:not(:empty) {
				background-color: #f7f7f7;
				border: 1px solid #e5e5e5;
				border-radius: 0.25em;
				padding: 0.5rem 1rem;
				margin-top: 0.5rem;
			}

			pick-at-random li button {
				background-color: transparent;
				border: 0;
				color: inherit;
				margin-inline-start: 0.5em;
				padding: 0;
			}

			pick-at-random li button svg {
				margin-bottom: -0.25em;
			}

			pick-at-random [clear-list] {
				background-color: transparent;
				border: 0;
				font: inherit;
				color: inherit;
				padding: 0;
			}
		</style>
	</head>

	<body>

		<h1>Pick at Random</h1>

		<pick-at-random
			add-label="Add a Person"
			add-button="Add Person"
			add-status='You added "${value}" to the list!'
			pick-button="Pick a Person"
			clear-button="or remove everyone"
			clear-all-confirm="You sure, bro?"
			remove-status="You have removed ${value}"
			selected-status='"${value}" has to drive!'
			remove-button="❌"
			remove-label="Get rid of ${value}"
			local-storage="designed-driver"
		></pick-at-random>

		<br><br><br><br>

		<pick-at-random local-storage="rando"></pick-at-random>


		<script>
			customElements.define('pick-at-random', class extends HTMLElement {

				/**
				 * Instantiate the component
				 */
				constructor () {

					// Inherits parent class properties
					super();

					// Create a unique ID for the instance
					this.uuid = `pick-${crypto.randomUUID()}`;

					// Settings
					this.settings = {
						addLabel: this.getAttribute('add-label') || 'Add an Item',
						addButton: this.getAttribute('add-button') || 'Add Item',
						pickButton: this.getAttribute('pick-button') || 'Pick an Item',
						clearButton: this.getAttribute('clear-button') || 'or remove all items',
						addStatus: this.getAttribute('add-status') || '"${value}" has been added to the list.',
						clearAllConfirm: this.getAttribute('clear-all-confirm') || 'Are you sure you want to do this? It cannot be undone.',
						removeStatus: this.getAttribute('remove-status') || '"${value}" has been removed from the list.',
						selectedStatus: this.getAttribute('selected-status') || 'You picked ${value}',
						removeButton: this.getAttribute('remove-button') || `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16" aria-hidden="true"><path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708"/></svg>`,
						removeLabel: this.getAttribute('remove-label') || 'Remove ${value}',
						localStorageID: this.getAttribute('local-storage')
					};

					// Render our initial HTML
					this.innerHTML =
						`<form>
							<label for="${this.uuid}">${this.settings.addLabel}</label>
							<input type="text" id="${this.uuid}">
							<button>${this.settings.addButton}</button>
						</form>
						<ul></ul>
						<p><button pick-item>${this.settings.pickButton}</button> <button clear-list>${this.settings.clearButton}</button></p>
						<div role="status" pick-result></div>`;

					// Get our elements
					this.form = this.querySelector('form');
					this.list = this.querySelector('ul');
					this.field = this.form.querySelector('input');
					this.pickBtn = this.querySelector('[pick-item]');
					this.result = this.querySelector('[pick-result]');

					// Listen for event
					this.form.addEventListener('submit', this);
					this.addEventListener('click', this);

					// Load list from localStorage
					this.loadItems();

				}

				/**
				 * Handle events
				 * @param  {Event} event The event object
				 */
				handleEvent (event) {
					this[`on${event.type}`](event);
				}

				/**
				 * Handle submit events
				 * @param  {Event} event The event object
				 */
				onsubmit (event) {

					// Stop the form from reloading the page
					event.preventDefault();

					// If there's no item to add, bail
					if (!this.field.value.length) return;

					// Create a list item
					this.createListItem(this.field.value);

					// Show a status message
					this.showStatus(this.settings.addStatus.replace('${value}', this.field.value));

					// Clear text from field
					this.field.value = '';

					// Save our list to localStorage
					this.saveItems();

				}

				/**
				 * Handle click event
				 * @param  {Event} event The event object
				 */
				onclick (event) {
					this.onPickButton(event);
					this.onRemove(event);
					this.onClearList(event);
				}

				/**
				 * Clear the list of items
				 * @param  {Event} event The event object
				 */
				onClearList (event) {

					// Only run on [clear-list] button
					if (!event.target.closest('[clear-list]')) return;

					// Double check the user actually wants to do this
					let doClear = confirm(this.settings.clearAllConfirm);
					if (!doClear) return;

					// Clear the list
					this.list.innerHTML = '';

					// Remove items from localStorage
					this.removeItems();

				}

				/**
				 * Handle remove button click
				 * @param  {Event} event The event object
				 */
				onRemove (event) {

					// Only run on remove buttons
					let btn = event.target.closest('[data-remove]');
					if (!btn) return;
					let txt = btn.getAttribute('data-remove');

					// Get the list item
					let li = event.target.closest('li');
					if (!li) return;
					li.remove();

					// Show remove message
					this.showStatus(this.settings.removeStatus.replace('${value}', txt));

					// Save updated list
					this.saveItems();

				}

				/**
				 * Handle pick button click
				 * @param  {Event} event The event object
				 */
				onPickButton (event) {

					// Only run on [pick-item] button
					if (!event.target.closest('[pick-item]')) return;

					// Get all of the list items
					let items = this.getItems();
					if (!items.length) return;

					// Randomize the items
					this.shuffle(items);

					// Show the result
					this.result.textContent = this.settings.selectedStatus.replace('${value}', items[0]);

				}

				/**
				 * Create list item
				 * @param  {String} The text to add to the item
				 */
				createListItem (txt) {

					// Create list item
					let li = document.createElement('li');
					li.textContent = txt;

					// Create remove button
					let btn = document.createElement('button');
					btn.innerHTML = this.settings.removeButton;
					btn.setAttribute('aria-label', this.settings.removeLabel.replace('${value}', txt));
					btn.setAttribute('data-remove', txt);
					li.append(btn);

					this.list.append(li);

				}

				/**
				 * Get an array of user-added items
				 * @return {Array} The items
				 */
				getItems () {
					return Array.from(this.list.querySelectorAll('li')).map((item) => {
						return item.textContent.replace(this.settings.removeButton, '');
					});
				}

				/**
				 * Save items to localStorage
				 */
				saveItems () {
					if (!this.settings.localStorageID) return;
					let items = JSON.stringify(this.getItems());
					localStorage.setItem(`pickAtRandom_${this.settings.localStorageID}`, items);
				}

				/**
				 * Remove items to localStorage
				 */
				removeItems () {
					if (!this.settings.localStorageID) return;
					localStorage.removeItem(`pickAtRandom_${this.settings.localStorageID}`);
				}

				/**
				 * Load saved list from localStorage
				 */
				loadItems () {
					if (!this.settings.localStorageID) return;
					let items = JSON.parse(localStorage.getItem(`pickAtRandom_${this.settings.localStorageID}`));
					if (!items) return;
					for (let item of items) {
						this.createListItem(item);
					}
				}

				/**
				 * Show a status message in the form
				 * @param  {String} msg The message to display
				 */
				showStatus (msg) {

					// Create a notification
					let notification = document.createElement('div');
					notification.setAttribute('role', 'status');

					// Inject it into the DOM
					this.form.append(notification);

					// Add text after it's in the UI
					setTimeout(function () {
						notification.textContent = msg;
					}, 1);

					// Remove it after 4 seconds
					setTimeout(function () {
						notification.remove();
					}, 4000);

				}

				/**
				 * Randomly shuffle an array
				 * https://stackoverflow.com/a/2450976/1293256
				 * @param  {Array} array The array to shuffle
				 * @return {Array}       The shuffled array
				 */
				shuffle (array) {

					let currentIndex = array.length;
					let temporaryValue, randomIndex;

					// While there remain elements to shuffle...
					while (0 !== currentIndex) {
						// Pick a remaining element...
						randomIndex = Math.floor(Math.random() * currentIndex);
						currentIndex -= 1;

						// And swap it with the current element.
						temporaryValue = array[currentIndex];
						array[currentIndex] = array[randomIndex];
						array[randomIndex] = temporaryValue;
					}

					return array;

				}

			});
		</script>
	</body>
</html>