From d9ca9a23b47173230d771d0f4514bbd48a71e5d2 Mon Sep 17 00:00:00 2001 From: BohdanNAUKMA Date: Sat, 16 May 2026 23:35:32 +0300 Subject: [PATCH 1/2] feat: Update index.html, main.css --- index.html | 77 ++++++++-- main.css | 412 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 464 insertions(+), 25 deletions(-) diff --git a/index.html b/index.html index 5971bf7d..323a294f 100644 --- a/index.html +++ b/index.html @@ -1,16 +1,75 @@ - - + + - My Page - - + + Кошик покупок - - Apple - 4 - + +
+
+
+ + +
+ +
+
+ Помідори +
+ 2 + +
+
+ +
+ Печиво +
+ + 2 + + + +
+
+ +
+ +
+ + 1 + + + +
+
+
+
+ +
+
+

Залишилося

+
+ Печиво 2 + Сир 1 +
+ +
+ +

Куплено

+
+ Помідори 2 +
+
+
+
+ +
+ Buy List + Виконано: Мартинов Богдан +
+ \ No newline at end of file diff --git a/main.css b/main.css index 00bc872e..758ab52f 100644 --- a/main.css +++ b/main.css @@ -1,17 +1,397 @@ -.product-item { - background-color: gray; - display: inline-block; - height: 25px; - padding: 5px; - border-radius: 5px; -} - -.amount { - background-color: yellow; - border-radius: 10px; - - display: inline-block; - height: 20px; - width: 20px; - text-align: center; + +* { + box-sizing: border-box; + margin: 0; + padding: 0; + font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; +} + +body +{ + background-color: #e5e5e5; + min-height: 100vh; + padding: 40px 20px; +} + + +.app-container +{ + max-width: 1000px; + margin: 0 auto; + display: flex; + gap: 24px; + align-items: flex-start; +} + +.products-section +{ + flex: 2; + background: #ffffff; + border-radius: 8px; + padding: 16px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); +} + +.summary-section +{ + flex: 1; +} + +.summary-card +{ + background: #ffffff; + border-radius: 8px; + padding: 20px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); +} + + +.add-item-box +{ + display: flex; + gap: 12px; + border-bottom: 2px solid #f0f0f0; + padding-bottom: 16px; + margin-bottom: 8px; +} + +.input-field, .input-edit +{ + flex: 1; + padding: 10px 14px; + border: 1px solid #dcdcdc; + border-radius: 4px; + font-size: 16px; + outline: none; +} + +.input-edit +{ + border-color: #4285f4; + max-width: 200px; +} + +.input-field:focus, .input-edit:focus +{ + border-color: #4285f4; + box-shadow: 0 0 0 2px rgba(66, 133, 244, 0.2); +} + + +button +{ + cursor: pointer; + border: none; + outline: none; + font-size: 14px; + transition: background-color 0.2s ease, transform 0.1s ease; +} + +button:focus-visible +{ + outline: 3px solid #7b2cbf; + outline-offset: 2px; +} + +.btn-add +{ + background-color: #4285f4; + color: white; + padding: 10px 24px; + border-radius: 4px; + font-weight: bold; +} + +.btn-add:hover { background-color: #357ae8; } + + +.products-list +{ + display: flex; + flex-direction: column; +} + +.product-row +{ + display: flex; + justify-content: space-between; + align-items: center; + padding: 12px 0; + border-bottom: 1px solid #f0f0f0; +} + +.product-row:last-child +{ + border-bottom: none; +} + +.product-name +{ + font-size: 18px; + color: #333; +} + + + + +.controls +{ + display: grid; + grid-template-columns: 28px 28px 28px 100px 28px; + gap: 8px; + align-items: center; + justify-items: center; +} + +.amount-badge +{ + background-color: #e0e0e0; + color: #333; + width: 28px; + height: 28px; + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 4px; + font-weight: bold; + font-size: 14px; +} + +.btn-plus, .btn-minus +{ + width: 28px; + height: 28px; + border-radius: 50%; + color: white; + font-weight: bold; + font-size: 16px; + display: flex; + align-items: center; + justify-content: center; +} + + +.btn-plus { background-color: #22ba46; } +.btn-plus:hover { background-color: #1e9e3b; } + + +.btn-minus { background-color: #db2828; } +.btn-minus:hover { background-color: #b82222; } + + +.btn-minus.disabled +{ + background-color: #ef9f9e; + cursor: not-allowed; +} + +.btn-status +{ + width: 100%; + background-color: #eeeeee; + color: #444; + padding: 6px 0; + border-radius: 4px; + border: 1px solid #dcdcdc; + text-align: center; +} +.btn-status:hover { background-color: #e0e0e0; } + +.btn-status.active +{ + background-color: #f8f9fa; + box-shadow: inset 0 1px 3px rgba(0,0,0,0.1); +} + +.btn-delete +{ + background-color: #db2828; + color: white; + width: 28px; + height: 28px; + border-radius: 4px; + font-size: 18px; + display: flex; + align-items: center; + justify-content: center; +} +.btn-delete:hover { background-color: #b82222; } + + +.summary-card h2 +{ + font-size: 20px; + margin-bottom: 14px; + color: #111; +} + +.summary-divider +{ + border: none; + border-top: 1px solid #f0f0f0; + margin: 20px 0; +} + +.summary-tags +{ + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.product-tag +{ + background-color: #eeeeee; + padding: 6px 12px; + border-radius: 20px; + font-size: 14px; + display: inline-flex; + align-items: center; + gap: 6px; + color: #444; +} + +.product-tag.bought +{ + text-decoration: line-through; + color: #888; +} + + +.tag-count { + background-color: #f2711d; + color: white; + width: 18px; + height: 18px; + border-radius: 50%; + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 11px; + font-weight: bold; +} + + +[data-tooltip] +{ + position: relative; +} + +[data-tooltip]::after +{ + content: attr(data-tooltip); + position: absolute; + bottom: 100%; + left: 50%; + transform: translate(-50%, 8px) scale(0.6); + opacity: 0; + pointer-events: none; + transition: transform 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275), opacity 0.2s ease; + + background-color: #7b2cbf; + color: #ffffff; + padding: 6px 12px; + border-radius: 6px; + font-size: 12px; + font-weight: normal; + white-space: nowrap; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15); + margin-bottom: 8px; + z-index: 999; +} + +[data-tooltip]:hover::after, +[data-tooltip]:focus-visible::after +{ + opacity: 1; + transform: translate(-50%, 0) scale(1); +} + + + +.author-badge +{ + position: fixed; + bottom: 0; + left: 40px; + background-color: #5a189a; + color: #ffffff; + padding: 12px 24px; + border-radius: 12px 12px 0 0; + font-weight: bold; + font-size: 16px; + box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.15); + z-index: 1000; + cursor: default; + + height: 45px; + overflow: hidden; + display: flex; + flex-direction: column; + gap: 12px; + transition: height 0.3s ease, background-color 0.3s ease; +} + +.badge-text-author +{ + font-weight: normal; + font-size: 13px; + opacity: 0.9; +} + +.author-badge:hover, .author-badge:focus-within +{ + height: 75px; + background-color: #3c096c; +} + + +@media (max-width: 650px) +{ + body + { + padding: 16px 10px; + } + + .app-container + { + flex-direction: column; + gap: 16px; + } + + .products-section, .summary-section + { + width: 100%; + flex: none; + } +} + + +@media print +{ + .author-badge + { + position: absolute; + bottom: 20px; + left: 20px; + background: #ffffff; + color: #5a189a; + border: 2px solid #5a189a; + height: auto; + padding: 8px 16px; + border-radius: 8px; + box-shadow: none; + } + + .badge-text-main + { + display: none; + } + + .badge-text-author + { + display: block; + font-size: 16px; + font-weight: bold; + } } \ No newline at end of file From a239c0a06c3d4161ead0175292ba75644d9ac60c Mon Sep 17 00:00:00 2001 From: BohdanNAUKMA Date: Sat, 13 Jun 2026 00:17:48 +0300 Subject: [PATCH 2/2] js --- index.html | 2 +- main.js | 222 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 main.js diff --git a/index.html b/index.html index 323a294f..3752d31f 100644 --- a/index.html +++ b/index.html @@ -70,6 +70,6 @@

Куплено

Buy List Виконано: Мартинов Богдан - + \ No newline at end of file diff --git a/main.js b/main.js new file mode 100644 index 00000000..cb9fd28f --- /dev/null +++ b/main.js @@ -0,0 +1,222 @@ +document.addEventListener('DOMContentLoaded', () => { + let products = [ + { id: 1, name: 'Помідори', amount: 2, isBought: true }, + { id: 2, name: 'Печиво', amount: 2, isBought: false }, + { id: 3, name: 'Сир', amount: 1, isBought: false } + ]; + + let nextId = 4; + + const productsListElement = document.querySelector('.products-list'); + const inputField = document.querySelector('.input-field'); + const btnAdd = document.querySelector('.btn-add'); + const summaryTagsContainers = document.querySelectorAll('.summary-tags'); + + function renderList() + { + productsListElement.innerHTML = ''; + + products.forEach(product => { + productsListElement.appendChild(createProductRow(product)); + }); + + updateSummary(); + } + + function createProductRow(product) + { + const row = document.createElement('div'); + row.className = `product-row ${product.isBought ? 'bought' : ''}`; + + + const nameSpan = document.createElement('span'); + nameSpan.className = 'product-name'; + nameSpan.textContent = product.name; + + const nameInput = document.createElement('input'); + nameInput.type = 'text'; + nameInput.className = 'input-edit'; + nameInput.setAttribute('aria-label', 'Редагувати назву товару'); + + if (!product.isBought) + { + nameSpan.style.cursor = 'pointer'; + + nameSpan.addEventListener('click', () => { + row.replaceChild(nameInput, nameSpan); + nameInput.value = product.name; + nameInput.focus(); + }); + + const saveEdit = () => { + const newName = nameInput.value.trim(); + if (newName) + { + product.name = newName; + } + nameSpan.textContent = product.name; + + if (nameInput.parentNode === row) + { + row.replaceChild(nameSpan, nameInput); + } + updateSummary(); + }; + + nameInput.addEventListener('blur', saveEdit); + nameInput.addEventListener('keypress', (e) => { + if (e.key === 'Enter') saveEdit(); + }); + } + + row.appendChild(nameSpan); + + const controls = document.createElement('div'); + controls.className = 'controls'; + + if (product.isBought) + { + const amountBadge = document.createElement('span'); + amountBadge.className = 'amount-badge'; + amountBadge.style.gridColumn = '2'; + amountBadge.textContent = product.amount; + + const btnStatus = document.createElement('button'); + btnStatus.type = 'button'; + btnStatus.className = 'btn-status'; + btnStatus.style.gridColumn = '4'; + btnStatus.setAttribute('data-tooltip', 'Повернути в список залишившихся'); + btnStatus.textContent = 'Не куплено'; + + btnStatus.addEventListener('click', () => { + product.isBought = false; + renderList(); + }); + + controls.appendChild(amountBadge); + controls.appendChild(btnStatus); + } + else + { + const btnMinus = document.createElement('button'); + btnMinus.type = 'button'; + btnMinus.className = `btn-minus ${product.amount === 1 ? 'disabled' : ''}`; + if (product.amount === 1) + { + btnMinus.disabled = true; + } + btnMinus.setAttribute('aria-label', 'Зменшити кількість'); + btnMinus.setAttribute('data-tooltip', 'Мінімум 1'); + btnMinus.textContent = '-'; + btnMinus.addEventListener('click', () => { + if (product.amount > 1) + { + product.amount--; + renderList(); + } + }); + + const amountBadge = document.createElement('span'); + amountBadge.className = 'amount-badge'; + amountBadge.textContent = product.amount; + + const btnPlus = document.createElement('button'); + btnPlus.type = 'button'; + btnPlus.className = 'btn-plus'; + btnPlus.setAttribute('aria-label', 'Збільшити кількість'); + btnPlus.setAttribute('data-tooltip', 'Збільшити'); + btnPlus.textContent = '+'; + btnPlus.addEventListener('click', () => { + product.amount++; + renderList(); + }); + + const btnStatus = document.createElement('button'); + btnStatus.type = 'button'; + btnStatus.className = 'btn-status active'; + btnStatus.setAttribute('data-tooltip', 'Позначити як куплене'); + btnStatus.textContent = 'Куплено'; + btnStatus.addEventListener('click', () => { + product.isBought = true; + renderList(); + }); + + const btnDelete = document.createElement('button'); + btnDelete.type = 'button'; + btnDelete.className = 'btn-delete'; + btnDelete.setAttribute('aria-label', 'Видалити товар'); + btnDelete.setAttribute('data-tooltip', 'Видалити зі списку'); + btnDelete.textContent = '×'; + btnDelete.addEventListener('click', () => { + products = products.filter(p => p.id !== product.id); + renderList(); + }); + + controls.appendChild(btnMinus); + controls.appendChild(amountBadge); + controls.appendChild(btnPlus); + controls.appendChild(btnStatus); + controls.appendChild(btnDelete); + } + + row.appendChild(controls); + return row; + } + + function updateSummary() { + const leftContainer = summaryTagsContainers[0]; + const rightContainer = summaryTagsContainers[1]; + + leftContainer.innerHTML = ''; + rightContainer.innerHTML = ''; + + products.forEach(product => { + const tag = document.createElement('span'); + tag.className = `product-tag ${product.isBought ? 'bought' : ''}`; + + tag.appendChild(document.createTextNode(product.name + ' ')); + + const countSpan = document.createElement('span'); + countSpan.className = 'tag-count'; + countSpan.textContent = product.amount; + + tag.appendChild(countSpan); + + if (product.isBought) + { + rightContainer.appendChild(tag); + } + else + { + leftContainer.appendChild(tag); + } + }); + } + + function handleAddProduct() + { + const name = inputField.value.trim(); + if (name) + { + products.push({ + id: nextId++, + name: name, + amount: 1, + isBought: false + }); + inputField.value = ''; + inputField.focus(); + renderList(); + } + } + + btnAdd.addEventListener('click', handleAddProduct); + inputField.addEventListener('keypress', (e) => { + if (e.key === 'Enter') + { + handleAddProduct(); + } + }); + + renderList(); +}); \ No newline at end of file