Created
March 7, 2023 09:51
-
-
Save angusgrant/8e61335ebb983a33f42eaadbbc88620e to your computer and use it in GitHub Desktop.
Go Make Things. Structure and Scale. Week 5 Task 1. ES Modules
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>Dice Games | Seven Seas</title> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<link rel="stylesheet" type="text/css" href="style.css"> | |
</head> | |
<body> | |
<nav> | |
<a href="index.html">🏴☠️ Seven Seas</a> | |
<p class="description">The travel app for pirates</p> | |
</nav> | |
<h1>Dice Games</h1> | |
<p>Roll some dice, have some fun!</p> | |
<div class="dice-game"> | |
<roll-dice></roll-dice> | |
<roll-dice></roll-dice> | |
</div> | |
<script type="module" src="index.js"></script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Hold the treasure instance | |
let treasure; | |
/** | |
* Create new treasure instance | |
* @return {Constructor} A new TreasureChest instance | |
*/ | |
function createTreasure () { | |
// Get any saved loot from localStorage | |
let savedLoot = JSON.parse(localStorage.getItem('ss-treasure')); | |
// Create new Treasure Chest instance | |
treasure = new TreasureChest(savedLoot); | |
} | |
/** | |
* Display the amount of loot in the UI | |
*/ | |
function showLoot () { | |
let loot = document.querySelector('#loot'); | |
if (!loot) return; | |
loot.textContent = treasure.getLoot(); | |
} | |
/** | |
* Save loot to localStorage and update the UI | |
* @param {Event} event The event object | |
*/ | |
function saveLoot (event) { | |
// Create the treasure object | |
let treasure = { | |
gold: event.detail.getGold(), | |
silver: event.detail.getSilver(), | |
bronze: event.detail.getBronze() | |
}; | |
// Save it to localStorage | |
localStorage.setItem('ss-treasure', JSON.stringify(treasure)); | |
// Update the UI | |
showLoot(event.detail); | |
} | |
/** | |
* Handle treasure submit events | |
* @param {Event} event The event object | |
*/ | |
function submitHandler (event) { | |
// Get the coin type | |
// Only run on [data-treasure] forms | |
let coin = event.target.getAttribute('data-treasure'); | |
if (!coin) return; | |
// Stop the form from reloading the page | |
event.preventDefault(); | |
// Get coin value | |
let val = parseFloat(event.target.querySelector('[type="number"]').value); | |
if (!val) return; | |
// Add the correct loot | |
if (coin === 'gold') { | |
treasure.addGold(val); | |
} else if (coin === 'silver') { | |
treasure.addSilver(val); | |
} else if (coin === 'bronze') { | |
treasure.addBronze(val); | |
} | |
} | |
export {submitHandler, saveLoot}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>Seven Seas</title> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<link rel="stylesheet" type="text/css" href="style.css"> | |
</head> | |
<body> | |
<nav> | |
<a href="index.html">🏴☠️ Seven Seas</a> | |
<p class="description">The travel app for pirates</p> | |
</nav> | |
<h1>Seven Seas</h1> | |
<p id="loot"></p> | |
<p> | |
<a class="btn btn-secondary" href="dice.html">Dice Games</a> | |
<a class="btn" href="treasure.html">Treasure Chest</a> | |
</p> | |
<script type="module" src="index.js"></script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import TreasureChest from './TreasureChest.js'; | |
import RollDice from './RollDice.js'; | |
if ('customElements' in window) { | |
customElements.define('roll-dice', RollDice); | |
} | |
// | |
// Functions | |
// | |
// Hold the treasure instance | |
let treasure; | |
/** | |
* Create new treasure instance | |
* @return {Constructor} A new TreasureChest instance | |
*/ | |
function createTreasure () { | |
// Get any saved loot from localStorage | |
let savedLoot = JSON.parse(localStorage.getItem('ss-treasure')); | |
// Create new Treasure Chest instance | |
treasure = new TreasureChest(savedLoot); | |
} | |
/** | |
* Display the amount of loot in the UI | |
*/ | |
function showLoot () { | |
let loot = document.querySelector('#loot'); | |
if (!loot) return; | |
loot.textContent = treasure.getLoot(); | |
} | |
/** | |
* Save loot to localStorage and update the UI | |
* @param {Event} event The event object | |
*/ | |
function saveLoot (event) { | |
// Create the treasure object | |
let treasure = { | |
gold: event.detail.getGold(), | |
silver: event.detail.getSilver(), | |
bronze: event.detail.getBronze() | |
}; | |
// Save it to localStorage | |
localStorage.setItem('ss-treasure', JSON.stringify(treasure)); | |
// Update the UI | |
showLoot(event.detail); | |
} | |
/** | |
* Handle treasure submit events | |
* @param {Event} event The event object | |
*/ | |
function submitHandler (event) { | |
// Get the coin type | |
// Only run on [data-treasure] forms | |
let coin = event.target.getAttribute('data-treasure'); | |
if (!coin) return; | |
// Stop the form from reloading the page | |
event.preventDefault(); | |
// Get coin value | |
let val = parseFloat(event.target.querySelector('[type="number"]').value); | |
if (!val) return; | |
// Add the correct loot | |
if (coin === 'gold') { | |
treasure.addGold(val); | |
} else if (coin === 'silver') { | |
treasure.addSilver(val); | |
} else if (coin === 'bronze') { | |
treasure.addBronze(val); | |
} | |
} | |
/** | |
* Listen for loot events | |
* @param {Constructor} treasure The TreasureChest object | |
*/ | |
function lootListeners () { | |
document.addEventListener('submit', submitHandler); | |
document.addEventListener('treasure:gold', saveLoot); | |
document.addEventListener('treasure:silver', saveLoot); | |
document.addEventListener('treasure:bronze', saveLoot); | |
} | |
// | |
// Inits & Event Listeners | |
// | |
createTreasure(); | |
showLoot(); | |
lootListeners(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class RollDice extends HTMLElement { | |
#dice; | |
/** | |
* The constructor object | |
*/ | |
constructor () { | |
// Run this first | |
super(); | |
// Creates a shadow root | |
this.root = this.attachShadow({mode: 'closed'}); | |
// Define properties | |
this.#dice = [1, 2, 3, 4, 5, 6]; | |
// Render HTML | |
this.root.innerHTML = | |
`<style> | |
button { | |
background-color: var(--bg-color, #0088cc); | |
border: 1px solid var(--bg-color, #0088cc); | |
border-radius: var(--radius, 0.25em); | |
color: var(--color, #ffffff); | |
font-size: var(--size, 1.5em); | |
padding: 0.5em 1em; | |
} | |
[aria-live] { | |
font-size: var(--msg-size, 1.3125em); | |
font-weight: var(--msg-weight, normal); | |
font-style: var(--msg-style, normal); | |
color: var(--msg-color, inherit); | |
} | |
</style> | |
<p> | |
<button><slot>Roll Dice</slot></button> | |
</p> | |
<div aria-live="polite"></div>`; | |
} | |
/** | |
* 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; | |
} | |
/** | |
* Shuffle dice array and return first number | |
* @return {Number} The result | |
*/ | |
#roll () { | |
this.#shuffle(this.#dice); | |
return this.#dice[0]; | |
} | |
/** | |
* Handle click events | |
* @param {Event} event The event object | |
*/ | |
#clickHandler (event) { | |
// Get the host component | |
let host = event.target.getRootNode().host; | |
// Get the message element | |
let target = host.root.querySelector('[aria-live="polite"]'); | |
if (!target) return; | |
// Roll the dice | |
let roll = host.#roll(); | |
// Inject the message into the UI | |
target.textContent = `You rolled a ${roll}`; | |
} | |
/** | |
* Runs each time the element is appended to or moved in the DOM | |
*/ | |
connectedCallback () { | |
// Attach a click event listener to the button | |
let btn = this.root.querySelector('button'); | |
if (!btn) return; | |
btn.addEventListener('click', this.#clickHandler); | |
} | |
/** | |
* Runs when the element is removed from the DOM | |
*/ | |
disconnectedCallback () { | |
// Remove the click event listener from the button | |
let btn = this.root.querySelector('button'); | |
if (!btn) return; | |
btn.removeEventListener('click', this.#clickHandler); | |
} | |
} | |
export default RollDice; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Main Styles | |
*/ | |
body { | |
color: #272727; | |
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; | |
margin: 1em auto; | |
max-width: 44em; | |
width: 88%; | |
} | |
/** | |
* Add box sizing to everything | |
* @link http://www.paulirish.com/2012/box-sizing-border-box-ftw/ | |
*/ | |
*, | |
*:before, | |
*:after { | |
box-sizing: border-box; | |
} | |
p { | |
margin: 0 0 1.5em; | |
} | |
img { | |
height: auto; | |
max-width: 100%; | |
} | |
a { | |
color: #0088cc; | |
} | |
a:active, | |
a:hover { | |
color: #272727; | |
} | |
/** | |
* Nav Menu | |
*/ | |
@media (min-width: 40em) { | |
nav { | |
display: grid; | |
grid-template-columns: repeat(2, 1fr); | |
grid-template-rows: 1fr; | |
grid-column-gap: 2em; | |
grid-row-gap: 0; | |
} | |
nav .description { | |
text-align: right; | |
} | |
} | |
nav a { | |
color: #272727; | |
font-size: 1.2em; | |
font-weight: bold; | |
text-decoration: none; | |
} | |
nav a:active, | |
nav a:hover { | |
color: #0088cc; | |
} | |
nav .description { | |
color: #808080; | |
font-style: italic; | |
} | |
/** | |
* Buttons | |
*/ | |
button, | |
.btn { | |
background-color: #0088cc; | |
border: 1px solid #0088cc; | |
border-radius: 0.25em; | |
font-family: inherit; | |
color: #ffffff; | |
font-size: 1em; | |
margin-right: 0.5em; | |
padding: 0.5em 1em; | |
text-decoration: none; | |
} | |
button:hover, | |
.btn:hover { | |
background-color: #005c8a; | |
border-color: #005c8c; | |
color: #ffffff; | |
} | |
.btn-secondary { | |
background-color: #f7272f; | |
border-color: #f7272f; | |
} | |
.btn-secondary:hover { | |
background-color: #cb070e; | |
border-color: #cb070e; | |
} | |
.btn-tertiary { | |
background-color: #272727; | |
border-color: #272727; | |
} | |
.btn-tertiary:hover { | |
background-color: #808080; | |
border-color: #808080; | |
} | |
/** | |
* Typography | |
*/ | |
.text-large { | |
font-size: 1.2em; | |
} | |
/** | |
* Forms | |
*/ | |
label, | |
input { | |
display: block; | |
width: 100%; | |
} | |
input { | |
margin-bottom: 0.5em; | |
} | |
/** | |
* Dice Game | |
*/ | |
@media (min-width: 40em) { | |
.dice-game { | |
display: grid; | |
grid-template-columns: repeat(2, 1fr); | |
grid-template-rows: 1fr; | |
grid-column-gap: 2em; | |
grid-row-gap: 0; | |
} | |
} | |
/** | |
* Treasure Chest | |
*/ | |
#loot { | |
font-size: 1.2em; | |
font-weight: bold; | |
} | |
@media (min-width: 40em) { | |
.treasure-chest { | |
display: grid; | |
grid-template-columns: repeat(3, 1fr); | |
grid-template-rows: 1fr; | |
grid-column-gap: 2em; | |
grid-row-gap: 0; | |
} | |
} | |
[data-treasure] { | |
margin-bottom: 1.5em; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>Treasure Chest | Seven Seas</title> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<link rel="stylesheet" type="text/css" href="style.css"> | |
</head> | |
<body> | |
<nav> | |
<a href="index.html">🏴☠️ Seven Seas</a> | |
<p class="description">The travel app for pirates</p> | |
</nav> | |
<h1>Treasure Chest</h1> | |
<p id="loot" aria-live="polite"></p> | |
<div class="treasure-chest"> | |
<div> | |
<form data-treasure="gold"> | |
<label for="gold">Amount of Gold</label> | |
<input type="number" step="1" id="gold" value="1"> | |
<button class="btn btn-secondary">Add Gold</button> | |
</form> | |
</div> | |
<div> | |
<form data-treasure="silver"> | |
<label for="silver">Amount of Silver</label> | |
<input type="number" step="1" id="silver" value="1"> | |
<button class="btn btn-tertiary">Add Silver</button> | |
</form> | |
</div> | |
<div> | |
<form data-treasure="bronze"> | |
<label for="bronze">Amount of Bronze</label> | |
<input type="number" step="1" id="bronze" value="1"> | |
<button>Add Bronze</button> | |
</form> | |
</div> | |
</div> | |
<script type="module" src="index.js"></script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Libraries | |
// | |
class TreasureChest { | |
#bronze; | |
#silver; | |
#gold; | |
#loot; | |
/** | |
* Create the constructor object | |
* @param {Object} options User settings | |
*/ | |
constructor (options = {}) { | |
// Merge options into defaults | |
let {bronze, silver, gold, loot} = Object.assign({ | |
bronze: 0, | |
silver: 0, | |
gold: 0, | |
loot: `You have {{gold}} gold, {{silver}} silver, and {{bronze}} bronze.` | |
}, options); | |
// Set instance properties | |
this.#bronze = bronze; | |
this.#silver = silver; | |
this.#gold = gold; | |
this.#loot = loot; | |
} | |
/** | |
* Randomly shuffle an array | |
* https://stackoverflow.com/a/2450976/1293256 | |
* @param {Array} array The array to shuffle | |
* @return {Array} The shuffled array | |
*/ | |
static #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; | |
} | |
/** | |
* Emit a custom event | |
* @param {String} type The event type | |
* @param {*} detail Any details to pass along with the event | |
*/ | |
#emit (type, detail) { | |
// Create a new event | |
let event = new CustomEvent(type, { | |
bubbles: true, | |
cancelable: true, | |
detail: detail | |
}); | |
// Dispatch the event | |
return document.dispatchEvent(event); | |
} | |
/** | |
* Add bronze to the treasure chest | |
* @param {Number} n The amount to add | |
*/ | |
addBronze (n) { | |
this.#bronze += n; | |
this.#emit('treasure:bronze', this); | |
return this; | |
} | |
/** | |
* Add silver to the treasure chest | |
* @param {Number} n The amount to add | |
*/ | |
addSilver (n) { | |
this.#silver += n; | |
this.#emit('treasure:silver', this); | |
return this; | |
} | |
/** | |
* Add gold to the treasure chest | |
* @param {Number} n The amount to add | |
*/ | |
addGold (n) { | |
this.#gold += n; | |
this.#emit('treasure:gold', this); | |
return this; | |
} | |
/** | |
* Get the total amount of loot as a string | |
* @return {String} The total amount of loot | |
*/ | |
getLoot () { | |
return this.#loot.replace('{{gold}}', this.#gold).replace('{{silver}}', this.#silver).replace('{{bronze}}', this.#bronze); | |
} | |
/** | |
* Get the amount of bronze | |
* @return {Number} The amount | |
*/ | |
getBronze () { | |
return this.#bronze; | |
} | |
/** | |
* Get the amount of silver | |
* @return {Number} The amount | |
*/ | |
getSilver () { | |
return this.#silver; | |
} | |
/** | |
* Get the amount of gold | |
* @return {Number} The amount | |
*/ | |
getGold () { | |
return this.#gold; | |
} | |
/** | |
* Generate random treasure | |
* @return {Object} The amount and type of loot found | |
*/ | |
static random () { | |
// Amount and type | |
let amount = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50]; | |
let type = ['bronze', 'silver', 'gold']; | |
// Randomize the amounts | |
this.#shuffle(amount); | |
this.#shuffle(type); | |
// Return the random loot | |
return { | |
amount: amount[0], | |
type: type[0] | |
}; | |
} | |
} | |
export default TreasureChest; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment