From 0e801e38a3cd24327dc4ee880faea033dcb22c92 Mon Sep 17 00:00:00 2001 From: maxiroh Date: Sat, 16 May 2026 23:38:03 +0300 Subject: [PATCH 1/2] Add badge, tooltip, columns, print styles --- index.html | 89 ++++++++++++++--- main.css | 283 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 345 insertions(+), 27 deletions(-) diff --git a/index.html b/index.html index 5971bf7d..48ed6baa 100644 --- a/index.html +++ b/index.html @@ -1,16 +1,83 @@ - - - - My Page - - - + + + + + Buy List + - - Apple - 4 - + + +
+ Buy List + Created by:
Max Grygorenko
+
+ + +
+ + +
+
+ + +
+ +
    +
  • + Помідори +
    + + 2 + +
    + + +
  • + +
  • + Печиво +
    + + 2 + +
    + + +
  • + +
  • + Сир +
    + + 1 + +
    + + +
  • +
+
+ + + + +
+ \ No newline at end of file diff --git a/main.css b/main.css index 00bc872e..e07e5f1c 100644 --- a/main.css +++ b/main.css @@ -1,17 +1,268 @@ -.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; +*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } + +body { + font-family: sans-serif; + background: #f0f0f0; + min-height: 100vh; + padding: 24px 20px 80px; +} + +.app { + display: flex; + gap: 20px; + max-width: 1000px; + margin: 0 auto; + background: #e0e0e0; + border-radius: 12px; + padding: 20px; +} + +@media (max-width: 650px) { + .app { flex-direction: column; } +} + +.left-col { + flex: 2; + background: #fff; + border-radius: 10px; + padding: 16px; +} + +.add-row { + display: flex; + gap: 10px; + margin-bottom: 16px; +} + +.add-row input { + flex: 1; + padding: 10px 14px; + border: 1px solid #ccc; + border-radius: 6px; + font-size: 14px; + outline: none; +} +.add-row input:focus { border-color: #2979ff; } + +button { + position: relative; + cursor: pointer; + border: none; + border-radius: 6px; + font-size: 14px; + padding: 8px 14px; +} + +button::after { + content: attr(data-tooltip); + position: absolute; + bottom: calc(100% + 8px); + left: 50%; + transform: translateX(-50%) scaleY(0); + transform-origin: bottom center; + background: #6a0dad; + color: #fff; + padding: 5px 12px; + border-radius: 10px; + white-space: nowrap; + font-size: 12px; + opacity: 0; + transition: opacity 0.25s ease, transform 0.25s ease; + pointer-events: none; + z-index: 10; +} +button:hover::after { + opacity: 1; + transform: translateX(-50%) scaleY(1); +} + +/* Add button */ +.add-row button { + background: #2979ff; + color: #fff; + padding: 10px 20px; + font-size: 15px; +} +.add-row button:hover { background: #1565c0; } + +.item-list { + list-style: none; + display: flex; + flex-direction: column; + gap: 10px; +} + +.item { + display: flex; + align-items: center; + gap: 10px; + padding: 8px 4px; + border-bottom: 1px solid #eee; +} + +.item-name { + flex: 1; + font-size: 15px; +} +.item-name.bought { + text-decoration: line-through; + color: #aaa; +} + +/* Qty block */ +.qty-block { + display: flex; + align-items: center; + gap: 6px; +} + +.qty { + min-width: 28px; + text-align: center; + font-weight: bold; + font-size: 14px; + background: #f5f5f5; + border-radius: 4px; + padding: 2px 6px; +} + +.btn-circle { + width: 28px; + height: 28px; + border-radius: 50%; + font-size: 18px; + display: flex; + align-items: center; + justify-content: center; + color: #fff; + padding: 0; +} +.btn-minus { background: #e53935; } +.btn-plus { background: #43a047; } + +/* Buy button */ +.btn-buy { + background: #f5f5f5; + color: #333; + border: 1px solid #ccc; +} +.btn-buy.is-bought { + background: #e8f5e9; + color: #388e3c; + border-color: #a5d6a7; +} + +.btn-delete { + background: #e53935; + color: #fff; + width: 28px; + height: 28px; + display: flex; + align-items: center; + justify-content: center; + font-size: 14px; + border-radius: 4px; + padding: 0; +} + +.right-col { + flex: 1; + display: flex; + flex-direction: column; + gap: 16px; +} + +.summary-block { + background: #fff; + border-radius: 10px; + padding: 16px; +} +.summary-block h2 { font-size: 16px; margin-bottom: 10px; } + +.tag-list { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.tag { + background: #f0f0f0; + border-radius: 20px; + padding: 4px 10px; + font-size: 13px; + display: flex; + align-items: center; + gap: 6px; +} +.tag-count { + background: #ff6f00; + color: #fff; + border-radius: 50%; + width: 20px; + height: 20px; + display: flex; + align-items: center; + justify-content: center; + font-size: 11px; + font-weight: bold; +} + +.badge { + position: fixed; + bottom: 0; + left: 20px; + background: #4a0080; + color: #fff; + padding: 10px 20px; + border-radius: 10px 10px 0 0; + cursor: pointer; + overflow: hidden; + height: 42px; + transition: height 0.35s ease, background 0.35s ease; + z-index: 100; +} + +.badge:hover { + height: 82px; + background: #7b1fa2; +} + +.badge-title { + display: block; + font-weight: bold; + font-size: 15px; +} + +.badge-author { + display: block; + font-size: 13px; + margin-top: 8px; + opacity: 0; + transform: translateY(8px); + transition: opacity 0.3s ease 0.1s, transform 0.3s ease 0.1s; +} + +.badge:hover .badge-author { + opacity: 1; + transform: translateY(0); +} + +@media print { + .badge { + position: static; + background: #fff !important; + color: #000 !important; + border: 2px solid #4a0080; + border-radius: 10px; + height: auto !important; + padding: 10px 20px; + margin-bottom: 16px; + } + .badge-title { display: none; } + .badge-author { + opacity: 1 !important; + transform: none !important; + } + .add-row { display: none; } + .btn-minus, .btn-plus, .btn-buy, .btn-delete { display: none; } } \ No newline at end of file From 4e4e29eec68bd5ca7542e8ad0a80378ee148786e Mon Sep 17 00:00:00 2001 From: maxiroh Date: Sat, 23 May 2026 22:25:46 +0300 Subject: [PATCH 2/2] Add JS functionality --- index.html | 54 +++------------------- main.js | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+), 48 deletions(-) create mode 100644 main.js diff --git a/index.html b/index.html index 48ed6baa..099833ac 100644 --- a/index.html +++ b/index.html @@ -8,76 +8,34 @@ -
Buy List Created by:
Max Grygorenko
-
-
- - + +
- -
    -
  • - Помідори -
    - - 2 - -
    - - -
  • - -
  • - Печиво -
    - - 2 - -
    - - -
  • - -
  • - Сир -
    - - 1 - -
    - - -
  • -
+
    -
    + \ No newline at end of file diff --git a/main.js b/main.js new file mode 100644 index 00000000..8fc3eeed --- /dev/null +++ b/main.js @@ -0,0 +1,130 @@ +let items = [ + { id: 1, name: 'Помідори', qty: 2, bought: false }, + { id: 2, name: 'Печиво', qty: 1, bought: false }, + { id: 3, name: 'Сир', qty: 1, bought: false }, +]; + +let nextId = 4; + +const input = document.getElementById('item-input'); +const addBtn = document.getElementById('add-btn'); +const itemList = document.getElementById('item-list'); +const remainingList = document.getElementById('remaining-list'); +const boughtList = document.getElementById('bought-list'); + +function addItem() { + const name = input.value.trim(); + if (!name) return; + items.push({ id: nextId++, name, qty: 1, bought: false }); + input.value = ''; + input.focus(); + render(); +} + +addBtn.addEventListener('click', addItem); +input.addEventListener('keydown', e => { + if (e.key === 'Enter') addItem(); +}); + +function render() { + itemList.innerHTML = ''; + remainingList.innerHTML = ''; + boughtList.innerHTML = ''; + + items.forEach(item => { + const li = document.createElement('li'); + li.className = 'item'; + + const nameSpan = document.createElement('span'); + nameSpan.className = 'item-name' + (item.bought ? ' bought' : ''); + nameSpan.textContent = item.name; + + if (!item.bought) { + nameSpan.style.cursor = 'pointer'; + nameSpan.addEventListener('click', () => { + const inp = document.createElement('input'); + inp.type = 'text'; + inp.value = item.name; + inp.className = 'edit-input'; + li.replaceChild(inp, nameSpan); + inp.focus(); + + inp.addEventListener('blur', () => { + item.name = inp.value.trim() || item.name; + render(); + }); + inp.addEventListener('keydown', e => { + if (e.key === 'Enter') inp.blur(); + }); + }); + } + + li.appendChild(nameSpan); + + if (!item.bought) { + const qtyBlock = document.createElement('div'); + qtyBlock.className = 'qty-block'; + + const btnMinus = document.createElement('button'); + btnMinus.className = 'btn-circle btn-minus'; + btnMinus.textContent = '−'; + btnMinus.setAttribute('data-tooltip', 'Зменшити'); + btnMinus.disabled = item.qty <= 1; + btnMinus.addEventListener('click', () => { + if (item.qty > 1) { item.qty--; render(); } + }); + + const qtySpan = document.createElement('span'); + qtySpan.className = 'qty'; + qtySpan.textContent = item.qty; + + const btnPlus = document.createElement('button'); + btnPlus.className = 'btn-circle btn-plus'; + btnPlus.textContent = '+'; + btnPlus.setAttribute('data-tooltip', 'Збільшити'); + btnPlus.addEventListener('click', () => { + item.qty++; + render(); + }); + + qtyBlock.append(btnMinus, qtySpan, btnPlus); + li.appendChild(qtyBlock); + } + + const btnBuy = document.createElement('button'); + btnBuy.className = 'btn-buy' + (item.bought ? ' is-bought' : ''); + btnBuy.textContent = item.bought ? 'Куплено' : 'Не куплено'; + btnBuy.setAttribute('data-tooltip', item.bought ? 'Зробити не купленим' : 'Відмітити як куплений'); + btnBuy.addEventListener('click', () => { + item.bought = !item.bought; + render(); + }); + li.appendChild(btnBuy); + + if (!item.bought) { + const btnDel = document.createElement('button'); + btnDel.className = 'btn-delete'; + btnDel.textContent = '✕'; + btnDel.setAttribute('data-tooltip', 'Видалити'); + btnDel.addEventListener('click', () => { + items = items.filter(i => i.id !== item.id); + render(); + }); + li.appendChild(btnDel); + } + + itemList.appendChild(li); + + const tag = document.createElement('span'); + tag.className = 'tag'; + tag.innerHTML = `${item.name} ${item.qty}`; + + if (item.bought) { + boughtList.appendChild(tag); + } else { + remainingList.appendChild(tag); + } + }); +} + +render(); \ No newline at end of file