diff --git a/index.html b/index.html index 5971bf7d..4d25bee8 100644 --- a/index.html +++ b/index.html @@ -1,16 +1,88 @@ - - + + - My Page - - - + + BuyList + + - - Apple - 4 - + +
+
+

Кошик покупок

+ +
+ + + +
+ + +
+ + +
+ + + \ No newline at end of file diff --git a/main.css b/main.css index 00bc872e..25a3f765 100644 --- a/main.css +++ b/main.css @@ -1,17 +1,423 @@ -.product-item { - background-color: gray; - display: inline-block; - height: 25px; - padding: 5px; - border-radius: 5px; +* { + box-sizing: border-box; +} + +body { + margin: 0; + min-height: 100vh; + font-family: Arial, sans-serif; + background-color: #d9d9d9; + color: #111; +} + +.page { + width: min(100%, 980px); + margin: 0 auto; + padding: 28px 20px; + display: grid; + grid-template-columns: minmax(0, 1.45fr) minmax(180px, 0.75fr); + gap: 18px; + align-items: start; +} + +.products-card, .summary-card { + background-color: white; + border-radius: 5px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12); +} + +.products-card { + overflow: visible; +} + +.summary-card { + overflow: hidden; +} + +.add-form { + padding: 14px; + display: flex; +} + +.add-form input { + flex: 1; + min-width: 0; + padding: 10px 14px; + font-size: 16px; + border: 1px solid #d7d7d7; + border-radius: 5px 0 0 5px; +} + +.add-form button { + padding: 0 40px; + border: none; + border-radius: 0 5px 5px 0; + background: linear-gradient(#3498db, #2b84c6); + color: white; + font-size: 16px; + font-weight: bold; + min-height: 46px; + transform: none; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 2px 0 #21679c, 0 2px 4px rgba(0, 0, 0, 0.16); +} + +.product>button:not(.delete), .product.product-bought>button:not(.delete) { + background: linear-gradient(#ffffff, #e7e7e7); + border: 1px solid #d4d4d4; + color: #555; + font-weight: bold; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9), 0 3px 0 #c4c4c4, 0 3px 6px rgba(0, 0, 0, 0.14); +} + +.products-list { + margin: 0; + padding: 0; + list-style: none; +} + +.product { + min-height: 72px; + padding: 16px; + border-top: 1px solid #ececec; + display: grid; + grid-template-columns: minmax(65px, 1fr) minmax(0, 0.5fr) 118px minmax(0, 0.5fr) 110px 34px; + column-gap: 8px; + row-gap: 8px; + align-items: center; + justify-items: stretch; +} + +.product-bought { + grid-template-columns: minmax(65px, 1fr) minmax(0, 0.5fr) 118px minmax(0, 0.5fr) 110px 34px; +} + +.product-name { + grid-column: 1; + margin: 0; + font-size: 16px; +} + +.product-bought .product-name { + text-decoration: line-through; +} + +.product-bought .counter { + grid-column: 3; + justify-self: center; +} + +.editable { + width: 100%; + max-width: 160px; + min-width: 65px; + padding: 7px 10px; + border: 1px solid #58aef7; + border-radius: 4px; + background-color: white; + box-shadow: 0 0 0 2px rgba(88, 174, 247, 0.18), 0 0 8px rgba(88, 174, 247, 0.45); +} + +.editable:focus { + outline: none; + border-color: #58aef7; + box-shadow: 0 0 0 2px rgba(88, 174, 247, 0.18), 0 0 8px rgba(88, 174, 247, 0.45); +} + +.controls { + grid-column: 3; + margin: 0; + padding: 0; + list-style: none; + display: grid; + grid-template-columns: 34px 30px 34px; + gap: 10px; + justify-content: center; + align-items: center; +} + +.counter { + display: flex; + justify-content: center; + align-items: center; + width: 30px; + height: 30px; + background-color: #e8e8e8; + border-radius: 5px; + font-size: 15px; + font-weight: bold; + color: #555; +} + +button { + position: relative; + min-height: 34px; + cursor: pointer; + border: 1px solid #d7d7d7; + border-radius: 5px; + font-size: 14px; + background-color: #f5f5f5; + appearance: none; + box-shadow: none; +} + +.product>button:not(.delete) { + grid-column: 5; + justify-self: end; + width: auto; + min-width: 120px; + padding: 0 12px; + height: 40px; + white-space: nowrap; +} + +.product.product-bought>button:not(.delete) { + grid-column: 5 / 7; + justify-self: end; + width: auto; + min-width: 120px; + padding: 0 12px; + height: 40px; + white-space: nowrap; +} + +.product .delete { + grid-column: 6; +} + +.minus, .plus, .delete { + width: 34px; + height: 34px; + border: none; + border-radius: 50%; + color: white; + font-size: 20px; + font-weight: bold; +} + +.minus { + background: linear-gradient(#ef3434, #d71920); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 3px 0 #a9161b, 0 3px 6px rgba(0, 0, 0, 0.18); +} + +.minus.disabled { + background: linear-gradient(#f7b0b0, #ef9999); + box-shadow: none; + opacity: 0.75; + cursor: not-allowed; +} + +.plus { + background: linear-gradient(#25c858, #18a83f); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 3px 0 #128332, 0 3px 6px rgba(0, 0, 0, 0.18); +} + +.delete { + border-radius: 5px; + background: linear-gradient(#ef3434, #d71920); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 3px 0 #a9161b, 0 3px 6px rgba(0, 0, 0, 0.18); +} + +.summary-card { + overflow: hidden; +} + +.summary-section { + padding: 0; + border-bottom: 1px solid #e5e5e5; +} + +.summary-section:last-child { + border-bottom: none; +} + +.summary-section h2 { + margin: 0; + padding: 20px 16px 22px; + border-bottom: 1px solid #e5e5e5; + font-size: 24px; +} + +.summary-list { + margin: 0; + padding: 20px 16px 22px; + list-style: none; + display: flex; + flex-wrap: wrap; + gap: 10px; + align-items: center; +} + +.summary-list strong { + color: #555; + font-weight: bold; +} + +.summary-list li { + display: flex; + align-items: center; + gap: 8px; + padding: 10px 12px; + background-color: #e8e8e8; + border-radius: 6px; + font-size: 14px; } .amount { - background-color: yellow; - border-radius: 10px; - - display: inline-block; - height: 20px; - width: 20px; - text-align: center; + display: flex; + justify-content: center; + align-items: center; + min-width: 22px; + height: 22px; + padding: 0 7px; + background-color: #ff6f1a; + color: white; + border-radius: 50%; + font-size: 13px; + font-weight: bold; + position: relative; +} + +.bought-label strong { + text-decoration: line-through; +} + +.bought-label .amount::before { + content: ""; + position: absolute; + left: 7px; + right: 7px; + top: 50%; + height: 1.5px; + background-color: white; + transform: translateY(-50%); +} + +button[data-tooltip]::after { + content: attr(data-tooltip); + position: absolute; + left: 50%; + bottom: calc(100% + 8px); + transform: translateX(-50%) translateY(10px) scale(0.85); + display: flex; + justify-content: center; + align-items: center; + min-width: 130px; + min-height: 32px; + padding: 4px 12px; + background-color: #4b148c; + color: white; + border-radius: 12px; + text-align: center; + font-size: 11px; + line-height: 1.1; + white-space: normal; + opacity: 0; + visibility: hidden; + pointer-events: none; + transition: opacity 0.25s ease, transform 0.25s ease, visibility 0.25s ease; + z-index: 20; +} + +button[data-tooltip]:hover::after { + opacity: 1; + visibility: visible; + font-weight: bold; + transform: translateX(-50%) translateY(0) scale(1); +} + +.author-badge { + position: fixed; + left: 18px; + bottom: 0; + width: 155px; + height: 92px; + padding: 12px 10px; + text-align: center; + background-color: #4b148c; + color: white; + border-radius: 12px 12px 0 0; + transform: translateY(42px); + transition: 0.35s ease; + z-index: 20; +} + +.author-badge:hover { + transform: translateY(0); + background-color: #6d20c7; +} + +.badge-title, .badge-author { + margin: 0; + font-weight: bold; +} + +.badge-title { + font-size: 26px; + margin-bottom: 12px; +} + +.badge-author { + font-size: 14px; + line-height: 1.2; +} + +.visually-hidden { + position: absolute; + width: 1px; + height: 1px; + overflow: hidden; + clip-path: inset(50%); + white-space: nowrap; +} + +@media (max-width: 650px) { + + .page { + grid-template-columns: 1fr; + padding: 16px; + } + + .add-form { + flex-direction: column; + gap: 10px; + } + + .add-form input, .add-form button { + width: 100%; + border-radius: 5px; + } + + .product, .product-bought { + grid-template-columns: minmax(65px, 1fr) minmax(0, 0.5fr) 118px minmax(0, 0.5fr) 110px 34px; + } + + .controls { + justify-content: center; + } +} + +@media print { + + body { + background-color: white; + } + + .author-badge { + background-color: white; + color: #4b148c; + border: 2px solid #4b148c; + transform: none; + } + + .badge-title { + display: none; + } + + .add-form button, .minus, .plus, .delete, .product button { + box-shadow: none !important; + } + + .add-form button { + transform: none; + } } \ No newline at end of file diff --git a/main.js b/main.js new file mode 100644 index 00000000..16d8fbfd --- /dev/null +++ b/main.js @@ -0,0 +1,218 @@ +const form = document.querySelector(".add-form"); +const input = document.querySelector("#product-name"); +const productsList = document.querySelector(".products-list"); +const summarySections = document.querySelectorAll(".summary-section"); + +const STORAGE_KEY = "buy-list-products"; +let products = loadProducts(); + +render(); + +form.addEventListener("submit", (event) => { + event.preventDefault(); + const name = input.value.trim(); + if (!name) { + input.focus(); + return; + } + products.push({ + id: Date.now(), + name: name, + count: 1, + bought: false + }); + input.value = ""; + input.focus(); + saveAndRender(); +}); + +productsList.addEventListener("click", (event) => { + const button = event.target.closest("button"); + const name = event.target.closest(".product-name"); + if (name) { + startEditingName(name); + return; + } + if (!button) { + return; + } + + const productElement = button.closest(".product"); + const id = Number(productElement.dataset.id); + const product = products.find(item => item.id === id); + + if (!product) return; + if (button.classList.contains("delete")) products = products.filter(item => item.id !== id); + if (button.classList.contains("plus")) product.count++; + if (button.classList.contains("minus") && product.count > 1) product.count--; + if (button.dataset.action === "buy") product.bought = true; + if (button.dataset.action === "unbuy") product.bought = false; + + saveAndRender(); +}); + +function render() { + productsList.textContent = ""; + products.forEach(product => { + productsList.append(createProductElement(product)); + }); + renderSummary(); +} + +function createProductElement(product) { + const li = document.createElement("li"); + li.classList.add("product"); + li.dataset.id = product.id; + if (product.bought) { + li.classList.add("product-bought"); + } + const name = document.createElement("p"); + name.classList.add("product-name"); + name.textContent = product.name; + + const counter = document.createElement("output"); + counter.classList.add("counter"); + counter.textContent = product.count; + + if (product.bought) { + const unbuyButton = document.createElement("button"); + unbuyButton.type = "button"; + unbuyButton.textContent = "Не куплено"; + unbuyButton.dataset.action = "unbuy"; + unbuyButton.dataset.tooltip = "Повернути до покупок"; + li.append(name, counter, unbuyButton); + return li; + } + + const controls = document.createElement("menu"); + controls.classList.add("controls"); + + const minusLi = document.createElement("li"); + const minusButton = document.createElement("button"); + minusButton.type = "button"; + minusButton.textContent = "-"; + minusButton.classList.add("minus"); + minusButton.dataset.tooltip = "Зменшити кількість"; + + if (product.count === 1) { + minusButton.classList.add("disabled"); + minusButton.disabled = true; + minusButton.dataset.tooltip = "Мінімальна кількість"; + } + + minusLi.append(minusButton); + + const counterLi = document.createElement("li"); + counterLi.append(counter); + + const plusLi = document.createElement("li"); + const plusButton = document.createElement("button"); + plusButton.type = "button"; + plusButton.textContent = "+"; + plusButton.classList.add("plus"); + plusButton.dataset.tooltip = "Збільшити кількість"; + plusLi.append(plusButton); + controls.append(minusLi, counterLi, plusLi); + + const buyButton = document.createElement("button"); + buyButton.type = "button"; + buyButton.textContent = "Куплено"; + buyButton.dataset.action = "buy"; + buyButton.dataset.tooltip = "Позначити як куплене"; + + const deleteButton = document.createElement("button"); + deleteButton.type = "button"; + deleteButton.textContent = "×"; + deleteButton.classList.add("delete"); + deleteButton.dataset.tooltip = "Видалити товар"; + + li.append(name, controls, buyButton, deleteButton); + + return li; +} + +function startEditingName(nameElement) { + const productElement = nameElement.closest(".product"); + const id = Number(productElement.dataset.id); + const product = products.find(item => item.id === id); + + if (!product || product.bought) return; + + const editInput = document.createElement("input"); + editInput.classList.add("editable"); + editInput.value = product.name; + + nameElement.replaceWith(editInput); + editInput.focus(); + + editInput.addEventListener("blur", () => { + const newName = editInput.value.trim(); + if (newName) product.name = newName; + saveAndRender(); + }); + + editInput.addEventListener("keydown", (event) => { + if (event.key === "Enter") editInput.blur(); + }); +} + +function renderSummary() { + const notBoughtList = summarySections[0].querySelector(".summary-list"); + const boughtList = summarySections[1].querySelector(".summary-list"); + notBoughtList.textContent = ""; + boughtList.textContent = ""; + products.forEach(product => { + const item = createSummaryItem(product); + if (product.bought) { + item.classList.add("bought-label"); + boughtList.append(item); + } else { + notBoughtList.append(item); + } + }); +} + +function createSummaryItem(product) { + const li = document.createElement("li"); + const name = document.createElement("strong"); + name.textContent = product.name; + const amount = document.createElement("output"); + amount.classList.add("amount"); + amount.textContent = product.count; + li.append(name, amount); + return li; +} + +function saveAndRender() { + localStorage.setItem(STORAGE_KEY, JSON.stringify(products)); + render(); +} + +function loadProducts() { + const savedProducts = localStorage.getItem(STORAGE_KEY); + + if (savedProducts) { + return JSON.parse(savedProducts); + } + + return [ + { + id: 1, + name: "Помідори", + count: 2, + bought: true + }, + { + id: 2, + name: "Печиво", + count: 2, + bought: false + }, + { + id: 3, + name: "Сир", + count: 1, + bought: false + } + ]; +} \ No newline at end of file