diff --git a/submissions/webdevtoday/js-movie-seat-booking/app.js b/submissions/webdevtoday/js-movie-seat-booking/app.js new file mode 100644 index 0000000..9094a7f --- /dev/null +++ b/submissions/webdevtoday/js-movie-seat-booking/app.js @@ -0,0 +1,20 @@ +import { RowNumber } from './modules/RowNumber.js'; +import { Ticket } from './modules/Ticket.js'; +import { ShoppingBasket } from './modules/ShoppingBasket.js'; + +document.addEventListener('DOMContentLoaded', () => { + + const shoppingBasket = new ShoppingBasket(); + + document.forms.CinemaSeats.addEventListener('input', ( { target } ) => { + + const ticketRow = new RowNumber(target.name); + const ticketSeat = target.value; + + if (ticketRow.isNumber()) { + const ticket = new Ticket(ticketRow.getNumber(), ticketSeat); + shoppingBasket.toggleTicket(ticket); + } + + }); +}); diff --git a/submissions/webdevtoday/js-movie-seat-booking/index.html b/submissions/webdevtoday/js-movie-seat-booking/index.html new file mode 100644 index 0000000..4eaca8c --- /dev/null +++ b/submissions/webdevtoday/js-movie-seat-booking/index.html @@ -0,0 +1,334 @@ + + + + + + HTML Movie Seat Booking + + + +
+
+
+
+

Choose your seats

+ +
+
+
+
+
+
+
N/A
+
Selected
+
A
+
+
+
+
+
+
+
+
+ A seat in cinema number +
+ +
+ + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+ +
+
+
+ + + + + + +
+
+ + + + + + + + + + + + +
+
+ + + + + + +
+
+
+
+
+
+

You are buying:

+
+
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    + + + diff --git a/submissions/webdevtoday/js-movie-seat-booking/modules/RowNumber.js b/submissions/webdevtoday/js-movie-seat-booking/modules/RowNumber.js new file mode 100644 index 0000000..4737950 --- /dev/null +++ b/submissions/webdevtoday/js-movie-seat-booking/modules/RowNumber.js @@ -0,0 +1,20 @@ +class RowNumber { + constructor(rowStr) { + this.sourceString = rowStr; + this.getNumberOfRowFromString(); + } + + getNumberOfRowFromString() { + this.number = this.sourceString.match(/\d+/)[0]; + } + + isNumber() { + return typeof +this.number === "number" ? true : false; + } + + getNumber() { + return this.number; + } +} + +export { RowNumber }; diff --git a/submissions/webdevtoday/js-movie-seat-booking/modules/ShoppingBasket.js b/submissions/webdevtoday/js-movie-seat-booking/modules/ShoppingBasket.js new file mode 100644 index 0000000..2f08c51 --- /dev/null +++ b/submissions/webdevtoday/js-movie-seat-booking/modules/ShoppingBasket.js @@ -0,0 +1,131 @@ +class ShoppingBasket { + constructor() { + this.basketElement = document.querySelector('.ShoppingBasket'); + this.containerElement = this.basketElement.querySelector('.ShoppingBasket_container'); + this.listElement = this.basketElement.querySelector('.ShoppingBasket_list'); + this.countElement = this.basketElement.querySelector('.ShoppingBasket_count'); + this.costElement = this.basketElement.querySelector('.ShoppingBasket_cost'); + this.buttonElement = document.querySelector('.ShoppingBasketButton'); + this.buttonElementCount = this.buttonElement.querySelector('.ShoppingBasketButton_count'); + this.linkingTicketsToTheList = {}; + this.count = 0; + this.oneTicketCost = 50; + this.currency = 'UAH'; + } + + addEventListenerToButton() { + this.buttonElement.addEventListener('click', this.buttonClickHandler.bind(this)); + } + + removeEventListenerToButton() { + this.buttonElement.removeEventListener('click', this.buttonClickHandler.bind(this)); + } + + buttonClickHandler() { + this.buttonElementToggleActiveClass(); + } + + buttonElementToggleActiveClass() { + document.body.classList.toggle('lock-body'); + this.buttonElement.classList.toggle('ShoppingBasketButton--active'); + this.containerElement.classList.toggle('ShoppingBasket_container--active'); + } + + calculateCost() { + return this.oneTicketCost * this.count; + } + + makeCostString() { + return `${this.calculateCost()}${this.currency}`; + } + + makeTicketWord() { + return this.count > 1 ? 'tickets' : 'ticket'; + } + + makeCountString() { + return `${this.count} ${this.makeTicketWord()}`; + } + + insertCountToCountElements() { + this.countElement.innerHTML = this.makeCountString(); + this.buttonElementCount.innerHTML = this.count; + } + + insertCostToCostElement() { + this.costElement.innerHTML = this.makeCostString(); + } + + showBasket() { + this.basketElement.classList.add('ShoppingBasket--active'); + } + + hideBasket() { + this.basketElement.classList.remove('ShoppingBasket--active'); + } + + showButtonElementCount() { + this.buttonElementCount.classList.add('ShoppingBasketButton_count--active'); + } + + hideButtonElementCount() { + this.buttonElementCount.classList.remove('ShoppingBasketButton_count--active'); + } + + createListElement(text) { + const li = document.createElement('li'); + li.className = 'ShoppingBasket_item'; + li.innerHTML = text; + return li; + } + + countTickets() { + return this.count; + } + + increaseCount() { + this.count++; + } + + decreaseCount() { + this.count--; + } + + toggleTicket(ticket) { + if (this.linkingTicketsToTheList.hasOwnProperty(ticket.getIDString())) { + this.removeTicket(ticket); + return; + } + this.appendTicket(ticket); + } + + appendTicket(ticket) { + const text = `${ticket.getRow()} row ${ticket.getSeat()} seat`; + const li = this.createListElement(text); + this.listElement.append(li); + this.linkingTicketsToTheList[ticket.getIDString()] = li; + this.increaseCount(); + if ( this.count === 1 ) { + this.showBasket(); + this.showButtonElementCount(); + this.addEventListenerToButton(); + } + this.insertCountToCountElements(); + this.insertCostToCostElement(); + this.removeEventListenerToButton(); + } + + removeTicket(ticket) { + this.linkingTicketsToTheList[ticket.getIDString()].remove(); + delete this.linkingTicketsToTheList[ticket.getIDString()]; + this.decreaseCount(); + if ( this.count < 1 ) { + this.hideBasket(); + this.hideButtonElementCount(); + } + this.insertCountToCountElements(); + this.insertCostToCostElement(); + } +} + +export { ShoppingBasket }; diff --git a/submissions/webdevtoday/js-movie-seat-booking/modules/Ticket.js b/submissions/webdevtoday/js-movie-seat-booking/modules/Ticket.js new file mode 100644 index 0000000..5b5fc13 --- /dev/null +++ b/submissions/webdevtoday/js-movie-seat-booking/modules/Ticket.js @@ -0,0 +1,21 @@ +class Ticket { + constructor(rowNum, seatNum) { + this.row = rowNum; + this.seat = seatNum; + this.IDString = `${this.getRow()}${this.getSeat()}`; + } + + getRow() { + return this.row; + } + + getSeat() { + return this.seat; + } + + getIDString() { + return this.IDString; + } +} + +export { Ticket }; diff --git a/submissions/webdevtoday/js-movie-seat-booking/style.css b/submissions/webdevtoday/js-movie-seat-booking/style.css new file mode 100644 index 0000000..87c3eeb --- /dev/null +++ b/submissions/webdevtoday/js-movie-seat-booking/style.css @@ -0,0 +1,381 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +img, svg { + max-width: 100%; + display: block; +} + +:root { + --max-width-content: 500px; + + --Main-bg-color: #252432; + + --Top-text-color: #c6c6cd; + + --Hint-text-color: #666684; + + --Hint-notAvailable-bg-color: #35334b; + --Hint-selected-bg-color: #37e7f7; + --Hint-available-bg-color: #ffffff; + + --CinemaScreen-bg-color: #f8f7f5; + + --Seat-notAvailable-bg-color: #444451; + --Seat-selected-bg-color: #37e7f7; + --Seat-focused-bg-color: rgb(255, 238, 0); + --Seat-available-bg-color: #ffffff; + --Seat-width: 30px; + --Seat-height: 25px; + + --Seats-gap: 10px; + --Seats-col-width: calc(var(--Seat-width) * 4 + var(--Seats-gap) * 4); + + --Button-bg-color: #44b755; +} + +html { + font-size: 16px; + font-family: Arial, Helvetica, sans-serif; +} + +html, body { + width: 100%; + min-height: 100vh; +} + +.sr-only { + position: absolute; + width: 1px; + overflow: hidden; + clip: rect(0 0 0 0); +} + +.lock-body { + overflow: hidden; +} + +.Main { + min-width: 360px; + min-height: 100vh; + background-color: var(--Main-bg-color); +} +.Main_container { + display: flex; + flex-direction: column; +} + +.Top { + text-align: center; + color: var(--Top-text-color); +} +.Top_container { + padding: 15px; +} +.Top_title { + font-size: 1.3em; + font-weight: 400; +} +.Top_button { + display: none; + position: fixed; + top: 0; + right: 0; + transform: translate(-10%, 10%); +} + +.Middle { + flex-grow: 1; +} +.Middle_shoppingBasket { + margin: 0 auto; + position: relative; + max-width: var(--max-width-content); +} + +.Hints_container { + margin: 0 auto; + padding: 30px 0 0; + display: flex; + justify-content: space-evenly; + max-width: var(--max-width-content); +} + +.Hint { + display: flex; + align-items: center; + color: var(--Hint-text-color); +} +.Hint::before { + content: ""; + margin: 0 15px 0 0; + display: block; + width: 30px; + height: 25px; +} +.Hint--notAvailable::before { + background-color: var(--Hint-notAvailable-bg-color); +} +.Hint--selected::before { + background-color: var(--Hint-selected-bg-color); +} +.Hint--available::before { + background-color: var(--Hint-available-bg-color); +} + +.CinemaScreen { + position: relative; + margin: 30px auto; + max-width: var(--max-width-content); + height: 150px; + overflow: hidden; + background-color: var(--Main-bg-color); +} +.CinemaScreen_layer { + position: absolute; + left: 50%; + width: 130%; + height: 100%; + border-radius: 50% 50% 0 0; + transform: translateX(-50%); +} +.CinemaScreen_mainLayer { + top: 0; + background: + linear-gradient(130deg, transparent 0%, transparent 40%, var(--CinemaScreen-bg-color) 40%, var(--CinemaScreen-bg-color) 77%, var(--Main-bg-color) 77%), + linear-gradient(55deg, var(--Main-bg-color) 0%, var(--Main-bg-color) 23%, var(--CinemaScreen-bg-color) 23%, var(--CinemaScreen-bg-color) 40%) +} +.CinemaScreen_bottomLayer { + top: 60%; + background-color: var(--Main-bg-color); +} + +.Seat--dropRight { + margin-right: calc(var(--Seat-width) + var(--Seats-gap)); +} +.Seat--dropLeft { + margin-left: calc(var(--Seat-width) + var(--Seats-gap)); +} +.Seat_checkbox { + position: absolute; + width: 1px; + height: 1px; + overflow: hidden; + clip: rect(0 0 0 0); +} +.Seat_view { + display: flex; + justify-content: center; + align-items: center; + width: var(--Seat-width); + height: var(--Seat-height); + background-color: var(--Seat-available-bg-color); +} +.Seat_checkbox + .Seat_view:hover { + cursor: pointer; +} +.Seat_checkbox:focus + .Seat_view { + border: 3px solid var(--Seat-focused-bg-color); +} +.Seat_checkbox:checked + .Seat_view { + background-color: var(--Seat-selected-bg-color); +} +.Seat_checkbox:checked:focus + .Seat_view { + border: 3px solid var(--Seat-focused-bg-color); +} +.Seat_checkbox:disabled + .Seat_view:hover { + cursor: not-allowed; +} +.Seat_checkbox:disabled + .Seat_view { + background-color: var(--Seat-notAvailable-bg-color); + color: #fff; +} + +.Seats { + border: none; +} +.Seats_row { + display: flex; + justify-content: center; +} +.Seats_col { + margin-bottom: calc(var(--Seat-height) + var(--Seats-gap) * 2); + display: flex; + flex-wrap: wrap; + flex-shrink: 0; + gap: var(--Seats-gap); + width: var(--Seats-col-width); +} +.Seats_col--leftSide { + padding: 0 var(--Seat-width); +} +.Seats_col--rightSide { + padding-left: calc(var(--Seat-width) + var(--Seats-gap) * 2); + padding-right: var(--Seat-width); +} + +.Button { + margin: 0 auto; + padding: 15px; + display: block; + width: 100%; + max-width: var(--max-width-content); + border: none; + color: #fff; + background-color: var(--Button-bg-color); + cursor: pointer; + transition: all .5s ease; +} +.Button:hover { + opacity: .7; +} + +.ShoppingBasket { + color: #fff; + opacity: 0; + transition: opacity .5s ease; +} +.ShoppingBasket--active { + opacity: 1; +} +.ShoppingBasket_container { + padding: 20px; + position: fixed; + top: 0; + right: calc(50% + var(--max-width-content) / 2); + height: 100vh; + display: flex; + flex-direction: column; + justify-content: center; +} +.ShoppingBasket_title { + padding: 0 0 5px 0; +} +.ShoppingBasket_list { + padding: 5px 0 5px 10px; + max-height: 78vh; + overflow-y: auto; + list-style-position: inside; + scrollbar-width: thin; +} +.ShoppingBasket_list::-webkit-scrollbar { + width: 7px; + background-color: #fff; +} +.ShoppingBasket_list::-webkit-scrollbar-button { + background-color: var(--Main-bg-color); + height: 10px; +} +.ShoppingBasket_list::-webkit-scrollbar-thumb { + background-color: var(--Main-bg-color); + border: 1px solid #fff; +} +.ShoppingBasket_total { + padding: 5px 0 0 0; + display: flex; + flex-wrap: wrap; + justify-content: space-between; +} +.ShoppingBasketButton { + width: 40px; + height: 40px; + border: none; + outline: none; + background: none; + z-index: 1; +} +.ShoppingBasketButton--active .ShoppingBasketButton_container::before { + content: "X"; + position: absolute; + top: 0; + left: 0; + width: 40px; + height: 40px; + display: flex; + justify-content: center; + align-items: center; + color: #fff; + font-size: 2.5em; +} +.ShoppingBasketButton--active .ShoppingBasketButton_svg { + display: none; +} +.ShoppingBasketButton--active .ShoppingBasketButton_count { + display: none; +} +.ShoppingBasketButton_container { + position: relative; + width: 100%; + height: 100%; +} +.ShoppingBasketButton_svg { + fill: var(--Top-text-color); +} +.ShoppingBasketButton_count { + position: absolute; + bottom: 0; + left: 0; + width: 20px; + height: 20px; + display: inline-block; + font-weight: 700; + line-height: 1.5; + text-align: center; + border-radius: 50%; + opacity: 0; + color: #fff; + background-color: #ff0000; + transition: opacity .5s ease; +} +.ShoppingBasketButton_count--active { + opacity: 1; +} + +@media (min-width: 650px) and (max-width: 1000px) { + .ShoppingBasket_container { + position: static; + height: auto; + } +} + +@media (max-width: 649px) { + :root { + --Seat-width: 30px; + --Seat-height: 25px; + --Seats-gap: 5px; + } + .Top_button { + display: block; + } + .CinemaScreen { + height: 125px; + } + .CinemaScreen_mainLayer { + background: + linear-gradient(130deg, transparent 0%, transparent 40%, var(--CinemaScreen-bg-color) 40%, var(--CinemaScreen-bg-color) 73%, var(--Main-bg-color) 73%), + linear-gradient(55deg, var(--Main-bg-color) 0%, var(--Main-bg-color) 27%, var(--CinemaScreen-bg-color) 27%, var(--CinemaScreen-bg-color) 40%) + } + + .Seats_col--leftSide { + padding: 0 var(--Seat-width) 0 0; + width: calc(var(--Seats-col-width) - var(--Seat-width)); + } + .Seats_col--rightSide { + padding-right: 0; + width: calc(var(--Seats-col-width) - var(--Seat-width)); + } + + .ShoppingBasket_container { + top: 0; + right: -200%; + width: 100%; + background-color: var(--Main-bg-color); + transition: right .5s ease; + } + .ShoppingBasket_container--active { + right: 0; + } +}