diff --git a/index.html b/index.html index 5971bf7d..47c5dc25 100644 --- a/index.html +++ b/index.html @@ -1,16 +1,80 @@ - - + + - My Page - - + Buy List - - Apple - 4 - +
+
+
+ + +
+ + +
+ +
+
+

Залишилося

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

Куплено

+
+ Помідори 2 +
+
+
+
+ +
+ Buy List + Created by:
Vadim Dudar
+
+ + - \ No newline at end of file + diff --git a/main.css b/main.css index 00bc872e..8d45fdf9 100644 --- a/main.css +++ b/main.css @@ -1,17 +1,315 @@ -.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; -} \ No newline at end of file +body { + background-color: #e0e0e0; + font-family: Arial, sans-serif; + margin: 0; + padding: 20px; + display: flex; + justify-content: center; +} + +.container { + display: flex; + gap: 20px; + max-width: 1000px; + width: 100%; +} + +.left-panel { + background-color: white; + flex: 2; + border-radius: 4px; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); + padding: 20px; +} + +.add-item-container { + display: flex; + margin-bottom: 20px; + border-bottom: 1px solid #eee; + padding-bottom: 20px; +} + +.add-input { + flex: 1; + padding: 10px; + border: 1px solid #ccc; + border-radius: 4px 0 0 4px; + font-size: 16px; + outline: none; +} + +.add-button { + background-color: #3b78c4; + color: white; + border: none; + padding: 10px 20px; + font-size: 16px; + font-weight: bold; + border-radius: 0 4px 4px 0; + cursor: pointer; +} + +.items-list { + list-style: none; + padding: 0; + margin: 0; +} + +.item-row { + display: flex; + align-items: center; + justify-content: space-between; + padding: 15px 0; + border-bottom: 1px solid #eee; +} + +.item-row:last-child { + border-bottom: none; +} + +.item-title { + flex: 1; + font-size: 18px; +} + +.item-title-edit { + flex: 1; + font-size: 18px; + padding: 5px; + margin-right: 15px; + border: 1px solid #3b78c4; + border-radius: 4px; + outline: none; +} + +.crossed-out { + text-decoration: line-through; + color: #888; +} + +.quantity-controls { + display: flex; + align-items: center; + gap: 5px; + width: 120px; + justify-content: center; +} + +.qty-btn { + width: 32px; + height: 32px; + border: none; + border-radius: 50%; + color: white; + font-weight: bold; + font-size: 18px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; +} + +.qty-btn.minus { + background-color: #c9302c; +} + +.qty-btn.minus.faded { + background-color: #e59a98; + cursor: not-allowed; +} + +.qty-btn.plus { + background-color: #4cae4c; +} + +.quantity-value { + background-color: #e0e0e0; + padding: 5px 12px; + border-radius: 4px; + font-weight: bold; +} + +/* Кнопки дій */ +.action-buttons { + display: flex; + gap: 5px; + width: 140px; + justify-content: flex-end; +} + +.status-btn { + padding: 8px 12px; + background-color: #f0f0f0; + border: 1px solid #ccc; + border-radius: 4px; + cursor: pointer; + font-weight: bold; + color: #555; +} + +.delete-btn { + background-color: #c9302c; + color: white; + border: none; + border-radius: 4px; + padding: 8px 12px; + cursor: pointer; + font-weight: bold; +} + +.right-panel { + flex: 1; + display: flex; + flex-direction: column; + gap: 20px; +} + +.stats-card { + background-color: white; + border-radius: 4px; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); + padding: 20px; +} + +.stats-card h2 { + margin-top: 0; + font-size: 20px; + border-bottom: 1px solid #eee; + padding-bottom: 15px; + margin-bottom: 15px; +} + +.stats-items-list { + display: flex; + flex-wrap: wrap; + gap: 10px; +} + +.stat-badge { + background-color: #e6e6e6; + padding: 5px 10px; + border-radius: 4px; + font-size: 14px; + display: flex; + align-items: center; + gap: 8px; +} + +.stat-qty { + background-color: #e67e22; + color: white; + border-radius: 50%; + width: 20px; + height: 20px; + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 12px; + font-weight: bold; +} + +[data-tooltip] { + position: relative; +} + +[data-tooltip]::after { + content: attr(data-tooltip); + position: absolute; + bottom: calc(100% + 6px); + left: 50%; + transform: translateX(-50%) scale(0); + transform-origin: bottom center; + background-color: #6a0dad; + color: white; + padding: 6px 12px; + border-radius: 6px; + font-size: 13px; + font-weight: normal; + white-space: nowrap; + opacity: 0; + pointer-events: none; + transition: transform 0.25s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.25s ease; + z-index: 100; + box-shadow: 0 2px 6px rgba(0,0,0,0.2); +} + +[data-tooltip]:hover::after { + opacity: 1; + transform: translateX(-50%) scale(1); +} + +@media screen and (max-width: 650px) { + .container { + flex-direction: column; + } +} + +.author-badge { + position: fixed; + bottom: 0; + left: 20px; + transform: translateY(calc(100% - 45px)); + background-color: #9c00d4; + color: white; + padding: 10px 20px; + border-radius: 12px 12px 0 0; + transition: transform 0.4s ease, background-color 0.4s ease; + text-align: center; + z-index: 1000; + box-shadow: 0 -2px 10px rgba(0,0,0,0.15); + font-family: Arial, sans-serif; +} + +.badge-title { + display: block; + font-size: 22px; + font-weight: bold; + line-height: 25px; + margin-bottom: 20px; +} + +.badge-author { + display: block; + font-size: 15px; + line-height: 1.4; + padding-bottom: 5px; +} + +.author-badge:hover { + transform: translateY(0); + background-color: #3b00bd; +} + +@media print { + body { + background-color: white !important; + } + + [data-tooltip]::after { + display: none !important; + } + + .author-badge { + position: static; + transform: translateY(0); + background-color: white !important; + color: #4a148c !important; + border: 2px solid #4a148c !important; + border-radius: 6px !important; + padding: 15px; + box-shadow: none; + display: inline-block; + margin-top: 30px; + } + + .badge-title { + display: none; + } + + .badge-author { + color: #4a148c !important; + margin: 0; + padding: 0; + font-weight: bold; + } +} diff --git a/main.js b/main.js new file mode 100644 index 00000000..9746888f --- /dev/null +++ b/main.js @@ -0,0 +1,191 @@ +document.addEventListener('DOMContentLoaded', () => { + const STATE_KEY = 'buyListState'; + + let items = []; + + const defaultItems = [ + { id: 1, name: 'Помідори', quantity: 2, isBought: true }, + { id: 2, name: 'Печиво', quantity: 2, isBought: false }, + { id: 3, name: 'Сир', quantity: 1, isBought: false } + ]; + + const savedState = localStorage.getItem(STATE_KEY); + if (savedState) { + try { + items = JSON.parse(savedState); + } catch (e) { + items = [...defaultItems]; + } + } else { + items = [...defaultItems]; + } + + const generateId = () => { + return items.length > 0 ? Math.max(...items.map(i => i.id)) + 1 : 1; + }; + + const saveState = () => { + localStorage.setItem(STATE_KEY, JSON.stringify(items)); + }; + + const addInput = document.querySelector('.add-input'); + const addButton = document.querySelector('.add-button'); + const itemsList = document.querySelector('.items-list'); + const statsUnbought = document.querySelectorAll('.stats-items-list')[0]; + const statsBought = document.querySelectorAll('.stats-items-list')[1]; + + const render = () => { + itemsList.innerHTML = ''; + statsUnbought.innerHTML = ''; + statsBought.innerHTML = ''; + + items.forEach(item => { + const li = document.createElement('li'); + li.className = 'item-row'; + + if (item.isBought) { + li.innerHTML = ` + ${escapeHtml(item.name)} +
+ ${item.quantity} +
+
+ +
+ `; + } else { + li.innerHTML = ` + ${escapeHtml(item.name)} +
+ + ${item.quantity} + +
+
+ + +
+ `; + } + + const unbuyBtn = li.querySelector('.unbuy-btn'); + if (unbuyBtn) unbuyBtn.addEventListener('click', () => toggleStatus(item.id)); + + const buyBtn = li.querySelector('.buy-btn'); + if (buyBtn) buyBtn.addEventListener('click', () => toggleStatus(item.id)); + + const deleteBtn = li.querySelector('.delete-btn'); + if (deleteBtn) deleteBtn.addEventListener('click', () => deleteItem(item.id)); + + const plusBtn = li.querySelector('.plus'); + if (plusBtn) plusBtn.addEventListener('click', () => changeQuantity(item.id, 1)); + + const minusBtn = li.querySelector('.minus'); + if (minusBtn) minusBtn.addEventListener('click', () => changeQuantity(item.id, -1)); + + const titleSpan = li.querySelector('.item-title:not(.crossed-out)'); + if (titleSpan) { + titleSpan.addEventListener('click', () => { + const input = document.createElement('input'); + input.type = 'text'; + input.className = 'item-title-edit'; + input.value = item.name; + + const saveEdit = () => { + const newName = input.value.trim(); + if (newName) { + editName(item.id, newName); + } else { + render(); + } + }; + + input.addEventListener('blur', saveEdit); + input.addEventListener('keypress', (e) => { + if (e.key === 'Enter') { + input.blur(); + } + }); + + li.replaceChild(input, titleSpan); + input.focus(); + input.select(); + }); + } + + itemsList.appendChild(li); + + const statBadge = document.createElement('span'); + statBadge.className = 'stat-badge' + (item.isBought ? ' crossed-out' : ''); + statBadge.innerHTML = `${escapeHtml(item.name)} ${item.quantity}`; + + if (item.isBought) { + statsBought.appendChild(statBadge); + } else { + statsUnbought.appendChild(statBadge); + } + }); + + saveState(); + }; + + const escapeHtml = (unsafe) => { + return unsafe.replace(/&/g, "&").replace(//g, ">"); + } + + const addItem = () => { + const name = addInput.value.trim(); + if (name) { + items.push({ + id: generateId(), + name: name, + quantity: 1, + isBought: false + }); + addInput.value = ''; + addInput.focus(); + render(); + } + }; + + const deleteItem = (id) => { + items = items.filter(item => item.id !== id); + render(); + }; + + const toggleStatus = (id) => { + const item = items.find(item => item.id === id); + if (item) { + item.isBought = !item.isBought; + render(); + } + }; + + const changeQuantity = (id, delta) => { + const item = items.find(item => item.id === id); + if (item) { + const newQty = item.quantity + delta; + if (newQty >= 1) { + item.quantity = newQty; + render(); + } + } + }; + + const editName = (id, newName) => { + const item = items.find(item => item.id === id); + if (item) { + item.name = newName; + render(); + } + }; + + addButton.addEventListener('click', addItem); + addInput.addEventListener('keypress', (e) => { + if (e.key === 'Enter') { + addItem(); + } + }); + + render(); +});