From 1563c16b0e6f8799be88759961661a52abd08a75 Mon Sep 17 00:00:00 2001 From: xirce Date: Fri, 19 Nov 2021 23:59:29 +0500 Subject: [PATCH] all tasks done. --- Beverage.js | 101 ++++++++++++++++++++++++++++++++++++++ ItemContainer.js | 27 +++++++++++ index.html | 83 +++++++++++++------------------- index.js | 123 +++++++++++++++++++++++++++++++++++++++++++++++ styles.css | 96 +++++++++++++++++++++++++++++++++++- 5 files changed, 379 insertions(+), 51 deletions(-) create mode 100644 Beverage.js create mode 100644 ItemContainer.js diff --git a/Beverage.js b/Beverage.js new file mode 100644 index 0000000..09cf87c --- /dev/null +++ b/Beverage.js @@ -0,0 +1,101 @@ +function createBeverage(options) { + const view = document.createElement('fieldset'); + view.className = 'beverage'; + view.innerHTML = ` + × +

Напиток №${options.number}

+ +
+ Сделайте напиток на + + + + +
+
+ Добавьте к напитку: + + + + +
+ `; + return view; +} + +class Beverage { + view; + #container; + #number; + #numberView; + #variableElements; + + constructor(options) { + this.view = createBeverage(options); + this.#number = options.number; + this.#container = options.container; + this.#numberView = this.view.getElementsByClassName('number')[0]; + this.#variableElements = Array.from(this.view.getElementsByTagName('input')); + + const wishesInput = this.view.querySelector('.wishes'); + const wishesResult = this.view.querySelector('.wishes-result') + wishesInput.addEventListener('input', event => { + const regex = /срочно|быстрее|побыстрее|скорее|поскорее|очень нужно/gmi; + wishesResult.innerHTML = event.target.value.replace(regex, `$&`); + }); + + const deleteButton = this.view.querySelector('.btn-delete'); + deleteButton.addEventListener('click', () => { + this.#container ? this.#container.remove(this.#number - 1) : this.view.remove(); + }); + } + + set number(value) { + if (value < 0) { + throw 'incorrect number'; + } + this.#number = value; + this.#updateView(); + } + + #updateView() { + this.#numberView.textContent = this.#number; + this.#variableElements.forEach(element => element.name = element.name.replace(/\d+/, this.#number)); + } +} diff --git a/ItemContainer.js b/ItemContainer.js new file mode 100644 index 0000000..10f20ef --- /dev/null +++ b/ItemContainer.js @@ -0,0 +1,27 @@ +class ItemContainer { + #source = []; + #view; + + constructor(view) { + this.#view = view; + } + + get count() { + return this.#source.length; + } + + append(item) { + this.#source.push(item); + this.#view.append(item.view); + } + + remove(index) { + if (this.count > 1) { + const [removedItem] = this.#source.splice(index, 1); + removedItem.view.remove(); + for (let subIndex = index; subIndex < this.#source.length; subIndex++) { + this.#source[subIndex].number = subIndex + 1; + } + } + } +} diff --git a/index.html b/index.html index ddc8250..0f92928 100644 --- a/index.html +++ b/index.html @@ -6,56 +6,10 @@ -
-
-

Напиток №1

- -
- Сделайте напиток на - - - - -
-
- Добавьте к напитку: - - - - -
-
+ +
+
+
@@ -64,6 +18,35 @@

Напиток №1

+ + + + + + + + diff --git a/index.js b/index.js index e69de29..30406de 100644 --- a/index.js +++ b/index.js @@ -0,0 +1,123 @@ +const translation = { + 'espresso': 'эспрессо', + 'cappuccino': 'капучино', + 'cacao': 'какао', + 'usual': 'обычное', + 'no-fat': 'обезжиренное', + 'soy': 'соевое', + 'coconut': 'кокосовое', + 'whipped cream': 'взбитые сливки', + 'marshmallow': 'зефирки', + 'chocolate': 'шоколад', + 'cinnamon': 'корицу' +}; + +const form = document.querySelector('.beverage-form'); +const beverageList = document.querySelector('.beverage-list'); +const beverageContainer = new ItemContainer(beverageList); + +const addButton = document.querySelector('.add-button'); +const doneButton = document.querySelector('.submit-button'); + +const modal = document.querySelector('.confirmation'); +const modalConfirm = document.querySelector('.confirmation .btn-confirm'); +const modalClose = document.querySelector('.confirmation .btn-close'); + +const modalCounter = document.querySelector('.confirmation .modal-title .count'); +const table = modal.querySelector('table'); + +beverageContainer.append(new Beverage({ + container: beverageContainer, + number: 1 +})); + +addButton.addEventListener('click', () => { + beverageContainer.append(new Beverage({ + container: beverageContainer, + number: beverageContainer.count + 1, + })); +}); +doneButton.addEventListener('click', submitHandler); + +modalConfirm.addEventListener('click', modalConfirmHandle); +modalClose.addEventListener('click', () => modal.close()); + +function modalConfirmHandle() { + const deliverDateField = modal.querySelector('[name="deliver-date"]'); + const inputDate = new Date(deliverDateField.value); + if (inputDate < Date.now()) { + deliverDateField.style.backgroundColor = 'rgba(239,29,25,0.07)'; + alert('Мы не умеем перемещаться во времени. Выберите время позже, чем текущее'); + } else { + deliverDateField.style.backgroundColor = '#fff'; + modal.close() + } +} + +function submitHandler(event) { + event.preventDefault(); + renderConfirmation(); + modal.showModal(); +} + +function renderConfirmation() { + const formData = getFormData(form); + const count = formData.length; + const declinations = ['напиток', 'напитка', 'напитков']; + modalCounter.textContent = `${count} ${getDeclination(count, declinations)}`; + fillTable(table, formData); +} + +function getFormData(form) { + const beverageForms = Array.from(form.querySelectorAll('.beverage')); + return beverageForms.map(getBeverageData); +} + +function getBeverageData(beverageForm) { + const name = beverageForm.querySelector('[name="name"]'); + const milk = beverageForm.querySelector('[name^="milk-"]:checked'); + const options = Array.from(beverageForm.querySelectorAll('[name^="options-"]:checked')); + const wishes = beverageForm.querySelector('.wishes-result'); + + return { + name: name.value, + milk: milk.value, + options: options.map(option => option.value), + wishes: wishes.innerHTML + } +} + +function fillTable(table, formData) { + const content = formData.map(data => { + const row = document.createElement('tr'); + row.innerHTML = ` + ${translation[data.name.toLowerCase()]} + ${translation[data.milk.toLowerCase()]} + ${data.options.map(option => translation[option.toLowerCase()]).join(', ') || ''} + ${data.wishes} + `; + return row; + }); + + const oldTableBody = table.querySelector('tbody'); + oldTableBody && oldTableBody.remove(); + + const tableBody = table.createTBody(); + tableBody.append(...content); +} + +function getDeclination(number, declinations) { + let n = Math.abs(number); + n %= 100; + if (n >= 5 && n <= 20) { + return declinations[2]; + } + n %= 10; + if (n === 1) { + return declinations[0]; + } + if (n >= 2 && n <= 4) { + return declinations[1]; + } + return declinations[2]; +} diff --git a/styles.css b/styles.css index 68f5581..8700147 100644 --- a/styles.css +++ b/styles.css @@ -1,7 +1,51 @@ +@import url('https://fonts.googleapis.com/css2?family=Exo+2&display=swap'); + +* { + font-family: 'Exo 2', sans-serif; +} + +.beverage-list { + display: flex; + flex-flow: row; + justify-content: flex-start; + gap: 5px; + overflow-x: scroll; + overflow-y: hidden; +} + .beverage { + flex: 0 0 auto; + display: flex; + flex-direction: column; + align-items: flex-start; + max-width: 200px; border: 1px solid #eee; - margin: 15px 0; + margin: 0; padding: 15px; + background-color: rgba(125, 123, 124, 0.1); +} + +.beverage:hover, +.beverage:focus-within { + background-color: rgba(125, 123, 124, 0.4); +} + +.beverage .wishes { + display: block; + margin: 5px 0; + resize: none; +} + +.beverage .wishes-result { + margin: 0; + word-break: break-word; +} + +.btn-delete, .btn-close { + align-self: flex-end; + font-size: larger; + font-weight: bolder; + cursor: pointer; } .beverage-count { @@ -45,3 +89,53 @@ border-radius: 5px; } +dialog::backdrop { + background-color: rgba(0, 0, 0, 0.5); +} + +.confirmation { + box-sizing: border-box; + width: 500px; + padding: 5px; +} + +.confirmation .btn-close { + line-height: 0.5em; + align-self: flex-start; +} + +.modal-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 10px; + border-bottom: 1px solid gray; +} + +.modal-body { + margin-bottom: 10px; +} + +.modal-footer { + display: flex; + justify-content: space-between; +} + +.beverage-table { + border-collapse: collapse; +} + +.beverage-table th, +.beverage-table td { + padding: 5px; + border: 1px solid; +} + +.beverage-table td:nth-child(4) { + word-break: break-all; + max-width: 200px; +} + +.beverage-table th { + text-align: center; +}