diff --git a/client/model/Game.js b/client/model/Game.js index c80044b..763a34f 100644 --- a/client/model/Game.js +++ b/client/model/Game.js @@ -1,8 +1,9 @@ export class Game { - constructor(deck, hasTimer, moderatorName, timerParams=null) { + constructor(deck, hasTimer, hasDedicatedModerator, moderatorName, timerParams=null) { this.deck = deck; this.hasTimer = hasTimer; this.moderatorName = moderatorName; this.timerParams = timerParams; + this.hasDedicatedModerator = hasDedicatedModerator } } diff --git a/client/modules/GameCreationStepManager.js b/client/modules/GameCreationStepManager.js new file mode 100644 index 0000000..3bd2943 --- /dev/null +++ b/client/modules/GameCreationStepManager.js @@ -0,0 +1,464 @@ +import { Game } from "../model/Game.js"; +import { cancelCurrentToast, toast } from "./Toast.js"; +import { customCards } from "../config/customCards.js"; +import { ModalManager } from "./ModalManager.js"; + +export class GameCreationStepManager { + constructor(deckManager) { + this.step = 1; + this.currentGame = new Game(null, null, null, null); + this.deckManager = deckManager; + this.defaultBackHandler = () => { + cancelCurrentToast(); + this.removeStepElementsFromDOM(this.step); + this.decrementStep(); + this.renderStep("creation-step-container", this.step); + } + this.steps = { + 1: { + title: "Select your method of moderation:", + forwardHandler: () => { + if (this.currentGame.hasDedicatedModerator !== null) { + cancelCurrentToast(); + this.removeStepElementsFromDOM(this.step); + this.incrementStep(); + this.renderStep("creation-step-container", this.step); + } else { + toast("You must select a moderation option.", "error", true); + } + } + }, + 2: { + title: "Create your deck of cards:", + forwardHandler: () => { + if (this.deckManager.getDeckSize() >= 5) { + this.currentGame.deck = deckManager.getCurrentDeck().filter((card) => card.quantity > 0) + cancelCurrentToast(); + this.removeStepElementsFromDOM(this.step); + this.incrementStep(); + this.renderStep("creation-step-container", this.step); + } else { + toast("You must include enough cards for 5 players.", "error", true); + } + }, + backHandler: this.defaultBackHandler + }, + 3: { + title: "Set an optional timer:", + forwardHandler: () => { + let hours = parseInt(document.getElementById("game-hours").value); + let minutes = parseInt(document.getElementById("game-minutes").value); + if ((isNaN(hours) && isNaN(minutes)) + || (isNaN(hours) && minutes > 0 && minutes < 60) + || (isNaN(minutes) && hours > 0 && hours < 6) + || (hours === 0 && minutes > 0 && minutes < 60) + || (minutes === 0 && hours > 0 && hours < 6) + || (hours > 0 && hours < 6 && minutes >= 0 && minutes < 60) + ) { + if (hasTimer(hours, minutes)) { + this.currentGame.hasTimer = true; + this.currentGame.timerParams = { + hours: hours, + minutes: minutes + } + } else { + this.currentGame.hasTimer = false; + this.currentGame.timerParams = null; + } + console.log(this.currentGame); + cancelCurrentToast(); + this.removeStepElementsFromDOM(this.step); + this.incrementStep(); + this.renderStep("creation-step-container", this.step); + } else { + if (hours === 0 && minutes === 0) { + toast("You must enter a non-zero amount of time.", "error", true); + } else { + toast("Invalid timer options. Hours can be a max of 5, Minutes a max of 59.", "error", true); + } + } + }, + backHandler: this.defaultBackHandler + }, + 4: { + title: "Review and submit:", + backHandler: this.defaultBackHandler + } + } + } + + incrementStep() { + if (this.step < Object.keys(this.steps).length) { + this.step += 1; + } + } + + decrementStep() { + if (this.step > 1) { + this.step -= 1; + } + } + + renderStep(containerId, step) { + document.querySelectorAll('.animated-placeholder').forEach((el) => el.remove()); + document.querySelectorAll('.placeholder-row').forEach((el) => el.remove()); + document.getElementById("step-title").innerText = this.steps[step].title; + switch (step) { + case 1: + renderModerationTypeStep(this.currentGame, containerId, step); + showButtons(false, true, this.steps[step].forwardHandler, null); + break; + case 2: + renderRoleSelectionStep(this.currentGame, containerId, step, this.deckManager); + showButtons(true, true, this.steps[step].forwardHandler, this.steps[step].backHandler); + break; + case 3: + renderTimerStep(containerId, step, this.currentGame); + showButtons(true, true, this.steps[step].forwardHandler, this.steps[step].backHandler); + break; + case 4: + renderReviewAndCreateStep(containerId, step, this.currentGame); + showButtons(true, false, null, this.steps[step].backHandler); + break; + default: + break; + } + updateTracker(step); + } + + removeStepElementsFromDOM(stepNumber) { + document.getElementById('step-' + stepNumber)?.remove(); + } +} + +function renderModerationTypeStep(game, containerId, stepNumber) { + const stepContainer = document.createElement("div"); + setAttributes(stepContainer, {'id': 'step-' + stepNumber, 'class': 'flex-row-container'}); + + stepContainer.innerHTML = + "
I will be the dedicated mod. Don't deal me a card.
" + + "
The first person out will mod. Deal me into the game.
"; + + let dedicatedOption = stepContainer.querySelector('#moderation-dedicated'); + if (game.hasDedicatedModerator) { + dedicatedOption.classList.add('option-selected'); + } + let selfOption = stepContainer.querySelector('#moderation-self'); + if (game.hasDedicatedModerator === false) { + selfOption.classList.add('option-selected'); + } + + dedicatedOption.addEventListener('click', () => { + dedicatedOption.classList.add('option-selected'); + selfOption.classList.remove('option-selected'); + game.hasDedicatedModerator = true; + }); + + selfOption.addEventListener('click', () => { + selfOption.classList.add('option-selected'); + dedicatedOption.classList.remove('option-selected'); + game.hasDedicatedModerator = false; + }); + + document.getElementById(containerId).appendChild(stepContainer); +} + +function renderRoleSelectionStep(game, containerId, step, deckManager) { + const stepContainer = document.createElement("div"); + setAttributes(stepContainer, {'id': 'step-' + step, 'class': 'flex-row-container-left-align'}); + + let div = document.createElement("div"); + div.setAttribute("id", "deck-container"); + let deckContainer = document.createElement("div"); + deckContainer.setAttribute("id", "deck"); + + deckContainer = loadIncludedCards(deckManager, deckContainer); + + let deckLabel = document.createElement("label"); + deckLabel.setAttribute("for", "deck"); + deckLabel.innerText = 'Game Deck: ' + deckManager.getDeckSize() + ' Players'; + div.prepend(deckLabel); + div.appendChild(deckContainer); + stepContainer.appendChild(div); + + let customForm = loadCustomRoles(deckManager); + + stepContainer.prepend(customForm); + + document.getElementById(containerId).appendChild(stepContainer); + + $('.ui.dropdown') + .dropdown(); + + initializeRemainingEventListeners(deckManager); +} + +function renderTimerStep(containerId, stepNumber, game) { + let div = document.createElement("div"); + div.setAttribute("id", "step-" + stepNumber); + + let timeContainer = document.createElement("div"); + timeContainer.setAttribute("id", "game-time"); + + let hoursLabel = document.createElement("label"); + hoursLabel.setAttribute("for", "game-hours"); + hoursLabel.innerText = "Hours (max 5)" + let hours = document.createElement("input"); + setAttributes(hours, {type: "number", id: "game-hours", name: "game-hours", min: "0", max: "5", value: game.timerParams?.hours}) + + let minsLabel = document.createElement("label"); + minsLabel.setAttribute("for", "game-minutes"); + minsLabel.innerText = "Minutes" + let minutes = document.createElement("input"); + setAttributes(minutes, {type: "number", id: "game-minutes", name: "game-minutes", min: "1", max: "60", value: game.timerParams?.minutes}) + + timeContainer.appendChild(hoursLabel); + timeContainer.appendChild(hours); + timeContainer.appendChild(minsLabel); + timeContainer.appendChild(minutes); + div.appendChild(timeContainer); + + document.getElementById(containerId).appendChild(div); +} + +function renderReviewAndCreateStep(containerId, stepNumber, game) { + let div = document.createElement("div"); + div.setAttribute("id", "step-" + stepNumber); + + div.innerHTML = + "" + + "
" + + "" + + "
" + + "" + + "
"; + + div.querySelector('#mod-option').innerText = game.hasDedicatedModerator + ? "I will be the dedicated mod. Don't deal me a card." + : "The first person out will mod. Deal me into the game."; + + if (game.hasTimer) { + let formattedHours = !isNaN(game.timerParams.hours) + ? game.timerParams.hours + ' Hours' + : '0 Hours' + + let formattedMinutes = !isNaN(game.timerParams.minutes) + ? game.timerParams.minutes + ' Minutes' + : '0 Minutes' + + div.querySelector('#timer-option').innerText = formattedHours + " " + formattedMinutes; + } else { + div.querySelector('#timer-option').innerText = "untimed" + } + + for (let card of game.deck) { + let roleEl = document.createElement("div"); + roleEl.innerText = card.quantity + 'x ' + card.role; + div.querySelector('#roles-option').appendChild(roleEl); + } + + document.getElementById(containerId).appendChild(div); +} + +function setAttributes(element, attributeObject) { + for (let key of Object.keys(attributeObject)) { + element.setAttribute(key, attributeObject[key]); + } +} + +function updateTracker(step) { + document.querySelectorAll('.creation-step').forEach((element, i) => { + if ((i + 1) <= step) { + element.classList.add('creation-step-filled'); + } else { + element.classList.remove('creation-step-filled'); + } + }) +} + +function showButtons(back, forward, forwardHandler, backHandler) { + document.querySelector("#step-back-button")?.remove(); + document.querySelector("#step-forward-button")?.remove(); + if (back) { + let backButton = document.createElement("button"); + backButton.innerText = "Back"; + backButton.addEventListener('click', backHandler); + backButton.setAttribute("id", "step-back-button"); + document.getElementById("creation-step-buttons").appendChild(backButton); + } + + if (forward) { + let fwdButton = document.createElement("button"); + fwdButton.innerText = "Next"; + fwdButton.addEventListener('click', forwardHandler); + fwdButton.setAttribute("id", "step-forward-button"); + document.getElementById("creation-step-buttons").appendChild(fwdButton); + } +} + +// Display a widget for each default card that allows copies of it to be added/removed. Set initial deck state. +function loadIncludedCards(deckManager, deckContainer) { + + for (let i = 0; i < deckManager.getCurrentDeck().length; i ++) { // each dropdown should include every + let card = deckManager.getCurrentDeck()[i]; + let cardEl = constructCompactDeckBuilderElement(card, deckManager); + deckContainer.appendChild(cardEl); + } + + return deckContainer; +} + +/* Display a dropdown containing all the custom roles. Adding one will add it to the game deck and +create a widget for it */ +function loadCustomRoles(deckManager) { + let customContainer = document.createElement("div"); + customContainer.setAttribute("id", "custom-roles-container"); + + let formLabel = document.createElement("label"); + formLabel.innerText = 'Custom Roles'; + formLabel.setAttribute("for", "add-card-to-deck-form"); + + let createRoleButton = document.createElement("button"); + createRoleButton.setAttribute("id", "custom-role-btn"); + createRoleButton.innerText = 'Create Custom Role'; + + let form = document.createElement("form"); + form.setAttribute("id", "add-card-to-deck-form"); + + let selectEl = document.createElement("select"); + selectEl.setAttribute("id", "deck-select"); + selectEl.setAttribute("class", "ui search dropdown"); + + addOptionsToList(deckManager.getCurrentCustomRoleOptions(), selectEl); + + form.appendChild(selectEl); + + let submitBtn = document.createElement("input"); + submitBtn.setAttribute("type", "submit"); + submitBtn.setAttribute("value", "Include Role"); + submitBtn.addEventListener('click', (e) => { + e.preventDefault(); + if (selectEl.value && selectEl.value.length > 0) { + deckManager.addToDeck(selectEl.value); + let cardEl = constructCompactDeckBuilderElement(deckManager.getCard(selectEl.value), deckManager); + updateCustomRoleOptionsList(deckManager, selectEl); + document.getElementById("deck").appendChild(cardEl); + document.querySelector("#add-card-to-deck-form .text").innerText = ""; + } + }) + form.appendChild(submitBtn); + + customContainer.appendChild(formLabel); + customContainer.appendChild(form); + customContainer.appendChild(createRoleButton); + + return customContainer; +} + +function constructCompactDeckBuilderElement(card, deckManager) { + const cardContainer = document.createElement("div"); + + cardContainer.setAttribute("class", "compact-card"); + + cardContainer.setAttribute("id", "card-" + card.role.replaceAll(' ', '-')); + + cardContainer.innerHTML = + "
" + + "

-

" + + "
" + + "
" + + "

" + + "
" + + "
" + + "
" + + "

+

" + + "
"; + + cardContainer.querySelector('.card-role').innerText = card.role; + cardContainer.querySelector('.card-quantity').innerText = card.quantity; + + if (card.quantity > 0) { + cardContainer.classList.add('selected-card'); + } + + cardContainer.querySelector('.compact-card-right').addEventListener('click', () => { + deckManager.addCopyOfCard(card.role); + cardContainer.querySelector('.card-quantity').innerText = deckManager.getCard(card.role).quantity; + document.querySelector('label[for="deck"]').innerText = 'Game Deck: ' + deckManager.getDeckSize() + ' Players'; + if (deckManager.getCard(card.role).quantity > 0) { + document.getElementById('card-' + card.role.replaceAll(' ', '-')).classList.add('selected-card') + } + }); + cardContainer.querySelector('.compact-card-left').addEventListener('click', () => { + deckManager.removeCopyOfCard(card.role); + cardContainer.querySelector('.card-quantity').innerText = deckManager.getCard(card.role).quantity; + document.querySelector('label[for="deck"]').innerText = 'Game Deck: ' + deckManager.getDeckSize() + ' Players'; + if (deckManager.getCard(card.role).quantity === 0) { + document.getElementById('card-' + card.role.replaceAll(' ', '-')).classList.remove('selected-card') + } + }); + return cardContainer; +} + +function initializeRemainingEventListeners(deckManager) { + // document.getElementById("game-form").onsubmit = (e) => { + // e.preventDefault(); + // let timerBool = hasTimer(); + // let timerParams = timerBool + // ? { + // hours: document.getElementById("game-hours").value, + // minutes: document.getElementById("game-minutes").value + // } + // : null; + // if (deckManager.getDeckSize() >= 5) { + // createGameForHosting( + // deckManager.getCurrentDeck().filter((card) => card.quantity > 0), + // timerBool, + // document.getElementById("mod-name").value, + // timerParams + // ); + // } else { + // toast("You must include enough cards for 5 players.", "error", true); + // } + // } + document.getElementById("add-role-form").onsubmit = (e) => { + e.preventDefault(); + let name = document.getElementById("role-name").value.trim(); + let description = document.getElementById("role-description").value.trim(); + if (!deckManager.getCustomRoleOption(name)) { // confirm there is no existing custom role with the same name + deckManager.addToCustomRoleOptions({role: name, description: description}); + updateCustomRoleOptionsList(deckManager, document.getElementById("deck-select")) + ModalManager.dispelModal("add-role-modal", "add-role-modal-background"); + toast("Role Added", "success", true); + } else { + toast("There is already a custom role with this name.", "error", true); + } + } + document.getElementById("custom-role-btn").addEventListener( + "click", () => { + ModalManager.displayModal( + "add-role-modal", + "add-role-modal-background", + "close-modal-button" + ) + } + ) +} + +function updateCustomRoleOptionsList(deckManager, selectEl) { + document.querySelectorAll('#deck-select option').forEach(e => e.remove()); + addOptionsToList(deckManager.customRoleOptions, selectEl); +} + +function addOptionsToList(options, selectEl) { + for (let i = 0; i < options.length; i ++) { + let optionEl = document.createElement("option"); + optionEl.setAttribute("value", customCards[i].role); + optionEl.innerText = customCards[i].role; + selectEl.appendChild(optionEl); + } +} + +function hasTimer(hours, minutes) { + return (!isNaN(hours) || !isNaN(minutes)); +} diff --git a/client/modules/Toast.js b/client/modules/Toast.js index c5b5691..34585be 100644 --- a/client/modules/Toast.js +++ b/client/modules/Toast.js @@ -5,7 +5,7 @@ export const toast = (message, type, positionAtTop = true) => { }; function buildAndInsertMessageElement (message, type, positionAtTop) { - cancelCurrentMessage(); + cancelCurrentToast(); let backgroundColor; const position = positionAtTop ? 'top:4rem;' : 'bottom: 15px;'; switch (type) { @@ -27,7 +27,7 @@ function buildAndInsertMessageElement (message, type, positionAtTop) { document.body.prepend(messageEl); } -function cancelCurrentMessage () { +export function cancelCurrentToast () { const currentMessage = document.getElementById('current-info-message'); if (currentMessage !== null) { currentMessage.remove(); diff --git a/client/scripts/create.js b/client/scripts/create.js index bdd5018..b0260a1 100644 --- a/client/scripts/create.js +++ b/client/scripts/create.js @@ -1,16 +1,16 @@ -import { toast } from "../modules/Toast.js"; -import { ModalManager } from "../modules/ModalManager.js"; import { defaultCards } from "../config/defaultCards.js"; import { customCards } from "../config/customCards.js"; import { DeckStateManager } from "../modules/DeckStateManager.js"; import { XHRUtility } from "../modules/XHRUtility.js"; import { Game } from "../model/Game.js"; +import { GameCreationStepManager } from "../modules/GameCreationStepManager.js"; export const create = () => { let deckManager = new DeckStateManager(); + let gameCreationStepManager = new GameCreationStepManager(deckManager); loadDefaultCards(deckManager); - loadCustomCards(deckManager); - initializeRemainingEventListeners(deckManager); + loadCustomRoles(deckManager); + gameCreationStepManager.renderStep("creation-step-container", 1); } // Display a widget for each default card that allows copies of it to be added/removed. Set initial deck state. @@ -22,8 +22,6 @@ function loadDefaultCards(deckManager) { for (let i = 0; i < defaultCards.length; i ++) { // each dropdown should include every let card = defaultCards[i]; card.quantity = 0; - let cardEl = constructCompactDeckBuilderElement(defaultCards[i], deckManager); - document.getElementById("deck").appendChild(cardEl); deck.push(card); } deckManager.deck = deck; @@ -31,138 +29,13 @@ function loadDefaultCards(deckManager) { /* Display a dropdown containing all the custom roles. Adding one will add it to the game deck and create a widget for it */ -function loadCustomCards(deckManager) { - let form = document.getElementById("add-card-to-deck-form"); +function loadCustomRoles(deckManager) { customCards.sort((a, b) => { return a.role.localeCompare(b.role); }); - let selectEl = document.createElement("select"); - selectEl.setAttribute("id", "deck-select"); - selectEl.setAttribute("class", "ui search dropdown"); - addOptionsToList(customCards, selectEl); - form.appendChild(selectEl); - let submitBtn = document.createElement("input"); - submitBtn.setAttribute("type", "submit"); - submitBtn.setAttribute("value", "Include Role"); - submitBtn.addEventListener('click', (e) => { - e.preventDefault(); - if (selectEl.value && selectEl.value.length > 0) { - deckManager.addToDeck(selectEl.value); - let cardEl = constructCompactDeckBuilderElement(deckManager.getCard(selectEl.value), deckManager); - updateCustomRoleOptionsList(deckManager, selectEl); - document.getElementById("deck").appendChild(cardEl); - document.querySelector("#add-card-to-deck-form .text").innerText = ""; - } - }) - form.appendChild(submitBtn); - $('.ui.dropdown') - .dropdown(); deckManager.customRoleOptions = customCards; } -function initializeRemainingEventListeners(deckManager) { - document.getElementById("game-form").onsubmit = (e) => { - e.preventDefault(); - let timerBool = hasTimer(); - let timerParams = timerBool - ? { - hours: document.getElementById("game-hours").value, - minutes: document.getElementById("game-minutes").value - } - : null; - if (deckManager.getDeckSize() >= 5) { - createGameForHosting( - deckManager.getCurrentDeck().filter((card) => card.quantity > 0), - timerBool, - document.getElementById("mod-name").value, - timerParams - ); - } else { - toast("You must include enough cards for 5 players.", "error", true); - } - } - document.getElementById("add-role-form").onsubmit = (e) => { - e.preventDefault(); - let name = document.getElementById("role-name").value.trim(); - let description = document.getElementById("role-description").value.trim(); - if (!deckManager.getCustomRoleOption(name)) { // confirm there is no existing custom role with the same name - deckManager.addToCustomRoleOptions({role: name, description: description}); - updateCustomRoleOptionsList(deckManager, document.getElementById("deck-select")) - ModalManager.dispelModal("add-role-modal", "add-role-modal-background"); - toast("Role Added", "success", true); - } else { - toast("There is already a custom role with this name.", "error", true); - } - } - document.getElementById("custom-role-btn").addEventListener( - "click", () => { - ModalManager.displayModal( - "add-role-modal", - "add-role-modal-background", - "close-modal-button" - ) - } - ) -} - -function updateCustomRoleOptionsList(deckManager, selectEl) { - document.querySelectorAll('#deck-select option').forEach(e => e.remove()); - addOptionsToList(deckManager.customRoleOptions, selectEl); -} - -function addOptionsToList(options, selectEl) { - for (let i = 0; i < options.length; i ++) { - let optionEl = document.createElement("option"); - optionEl.setAttribute("value", customCards[i].role); - optionEl.innerText = customCards[i].role; - selectEl.appendChild(optionEl); - } -} - -function constructCompactDeckBuilderElement(card, deckManager) { - const cardContainer = document.createElement("div"); - - cardContainer.setAttribute("class", "compact-card"); - - cardContainer.setAttribute("id", "card-" + card.role.replaceAll(' ', '-')); - - cardContainer.innerHTML = - "
" + - "

-

" + - "
" + - "
" + - "

" + - "
0
" + - "
" + - "
" + - "

+

" + - "
"; - - cardContainer.querySelector('.card-role').innerText = card.role; - - cardContainer.querySelector('.compact-card-right').addEventListener('click', () => { - deckManager.addCopyOfCard(card.role); - cardContainer.querySelector('.card-quantity').innerText = deckManager.getCard(card.role).quantity; - document.querySelector('label[for="deck"]').innerText = 'Game Deck: ' + deckManager.getDeckSize() + ' Players'; - if (deckManager.getCard(card.role).quantity > 0) { - document.getElementById('card-' + card.role.replaceAll(' ', '-')).classList.add('selected-card') - } - }); - cardContainer.querySelector('.compact-card-left').addEventListener('click', () => { - deckManager.removeCopyOfCard(card.role); - cardContainer.querySelector('.card-quantity').innerText = deckManager.getCard(card.role).quantity; - document.querySelector('label[for="deck"]').innerText = 'Game Deck: ' + deckManager.getDeckSize() + ' Players'; - if (deckManager.getCard(card.role).quantity === 0) { - document.getElementById('card-' + card.role.replaceAll(' ', '-')).classList.remove('selected-card') - } - }); - return cardContainer; -} - -function hasTimer() { - return document.getElementById("game-hours").value.length > 0 || document.getElementById("game-minutes").value.length > 0 -} - function createGameForHosting(deck, hasTimer, modName, timerParams) { XHRUtility.xhr( '/api/games/create', diff --git a/client/scripts/home.js b/client/scripts/home.js index 4731090..87e4872 100644 --- a/client/scripts/home.js +++ b/client/scripts/home.js @@ -25,7 +25,9 @@ function attemptToJoinGame(code) { .then((res) => { if (res.status === 200) { window.location = '/game/' + res.content; - } else if (res.status === 404) { + } + }).catch((res) => { + if (res.status === 404) { toast("Game not found", "error", true); } else if (res.status === 400) { toast(res.content, "error", true); diff --git a/client/styles/GLOBAL.css b/client/styles/GLOBAL.css index bcfb95c..0efbcaf 100644 --- a/client/styles/GLOBAL.css +++ b/client/styles/GLOBAL.css @@ -43,6 +43,12 @@ h1 { margin: 0.5em 0; } +h2 { + color: whitesmoke; + font-size: 25px; + font-weight: bold; +} + h3 { color: #d7d7d7; font-family: 'signika-negative', sans-serif; @@ -91,6 +97,7 @@ button:active, input[type=submit]:active { display: flex; flex-direction: column; justify-content: center; + width: 100%; } button:hover, input[type="submit"]:hover { @@ -142,6 +149,83 @@ input { color: gray; } +.flex-row-container { + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-items: center; + justify-content: center; +} + +.flex-row-container-left-align { + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-items: center; +} + +.animated-placeholder { + animation-fill-mode: forwards; + animation-duration: 1s; + animation-iteration-count: infinite; + animation-name: pulse-background; + animation-timing-function: ease-in; + animation-direction: alternate; + background: rgb(238,238,238); + position: relative; + overflow: hidden; + height: 20px; + border-radius: 3px; + opacity: 0.15; + margin: 0.5em 0; +} + +.animated-placeholder-short { + width: 100%; + max-width: 15em; + height: 2em; + margin: 0 auto; +} + +.animated-placeholder-long { + width: 100%; + max-width: 30em; + height: 8em; + margin: 0 auto 0.5em auto; +} + +.animated-placeholder-invisible { + background-color: transparent; + animation: none; +} + +.placeholder-row { + display: flex; + margin: 0; + justify-content: center; +} + +.placeholder-row .animated-placeholder-short { + margin: 0 0 0.5em 0; +} + +@keyframes placeholder { + 0%{ + background-position: 50% 0 + } + 100%{ + background-position: -50% 0 + } +} + +@keyframes pulse-background { + from { + background-color: rgb(120 120 120); + } to { + background-color: rgb(255 255 255); + } +} + @keyframes fade-in-slide-down-then-exit { 0% { diff --git a/client/styles/create.css b/client/styles/create.css index c077ff0..92d1766 100644 --- a/client/styles/create.css +++ b/client/styles/create.css @@ -82,13 +82,19 @@ width: fit-content; } -#deck-container, #custom-roles-container { - margin: 0.5em 0; +#deck-container, #custom-roles-container, #step-3 { + margin: 1em 0; background-color: #1f1f1f; padding: 10px; border-radius: 3px; } +#step-3 { + display: flex; + justify-content: center; + align-items: center; +} + #deck { display: flex; flex-wrap: wrap; @@ -151,3 +157,99 @@ input[type="number"] { .dropdown { margin: 0.5em; } + +.creation-step { + width: 20px; + height: 20px; + background-color: transparent; + border-radius: 50%; + border: 2px solid whitesmoke; + margin: 0 0.5em; +} + +.creation-step-filled { + background-color: whitesmoke; +} + +#creation-step-container { + margin-top: 2em; + width: 100%; + min-height: 16em; +} + +#creation-step-container > div:nth-child(2) { + animation: fade-in 0.5s ease-out; +} + +#step-title { + margin: 0 auto 1em auto; + text-align: center; +} + +#creation-step-buttons { + display: flex; + justify-content: space-between; + margin-top: 2em; + position: relative; +} + +#creation-step-tracker { + display: flex; + justify-content: center; + margin-top: 2em; +} + +#step-forward-button, #step-back-button { + position: absolute; +} + +#step-forward-button { + right: 15%; +} + +#step-back-button { + left: 15%; +} + +#step-1 div { + background-color: black; + color: whitesmoke; + padding: 1em; + max-width: 20em; + margin: 0.5em; + cursor: pointer; + border: 2px solid transparent; + border-radius: 3px; + font-size: 25px; + box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.6); +} + +#step-1 div.option-selected { + border: 2px solid #0075F2; + background-color: #252730; +} + +#step-1 div > strong { + color: #0075F2; +} + +#step-1 div:hover { + border: 2px solid #0075F2; +} + +.review-option { + background-color: #1f1f1f; + color: whitesmoke; + padding: 10px; + width: fit-content; + border-radius: 3px; + margin: 0.5em 0; +} + +@keyframes fade-in { + from { + opacity: 0; + } to { + opacity: 1; + } +} diff --git a/client/views/create.html b/client/views/create.html index 906b51f..97a5ba1 100644 --- a/client/views/create.html +++ b/client/views/create.html @@ -47,36 +47,44 @@

Create A Game

-

- Creating a game gives you the moderator role with certain special permissions. You will not be dealt a card. -

-
- -
- +
+
+
+
+
-
- -
+
+

Select your method of moderation:

+
+
+
+
+
+
-
-
- -
- - - - -
-
-
- - -
- -
+
+ + + + + + + + + + + + + + + + + + + + + +