diff --git a/index.html b/index.html index 5971bf7..1a70cd7 100644 --- a/index.html +++ b/index.html @@ -1,16 +1,46 @@ - - + + - My Page - - - + + Кошик покупок + - - Apple - 4 - +
+ +
+
+ + +
+ +
+
+ + +
+
+

Залишилося

+
+
+
+
+
+

Куплено

+
+
+
+
+
+ + +
+
Buy List
+
Created by:
Denis Koriavets
+
+ + + \ No newline at end of file diff --git a/main.css b/main.css index 00bc872..bca7fa5 100644 --- a/main.css +++ b/main.css @@ -1,17 +1,557 @@ -.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; +:root { + --color-primary: #4285f4; + --color-primary-hover: #3367d6; + --color-delete: #ea4335; + --color-delete-hover: #d33b2c; + --color-delete-shadow: #b23c2a; + --color-plus: #34a853; + --color-plus-shadow: #257a3a; + --color-badge: #8717d6; + --color-badge-hover: #3d1bbd; + --color-qty: #ff6d00; + --color-border: #dadce0; + --color-divider: #f0f0f0; + --color-bg-light: #f8f9fa; + --color-bg-light-hover: #f1f3f4; + --color-bg-page: #e5e5e5; + --color-text: #333; + --color-text-secondary: #666; + --color-text-disabled: #888; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + background: var(--color-bg-page); + padding: 20px; + margin: 0; +} + +.container { + display: flex; + gap: 20px; + max-width: 1200px; + margin: 0 auto; +} + +.panel { + background: white; + border-radius: 8px; + padding: 20px; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); +} + +.left-panel { + flex: 2; +} + +.right-panel { + flex: 1; +} + +h3 { + margin-top: 0; + margin-bottom: 20px; + font-size: 24px; + font-weight: 600; + color: var(--color-text); +} + +.input-row { + display: flex; + gap: 10px; + margin-bottom: 30px; +} + +input[type="text"] { + flex: 1; + padding: 12px 16px; + border: 2px solid #e0e0e0; + border-radius: 6px; + font-size: 16px; + outline: none; + transition: border-color 0.2s; +} + +input[type="text"]:focus { + border-color: var(--color-primary); +} + +.add-btn { + background: var(--color-primary); + color: white; + border: none; + padding: 12px 24px; + border-radius: 6px; + font-weight: 600; + font-size: 16px; + cursor: pointer; + transition: background-color 0.2s; + box-shadow: 0 3px 0 0 var(--color-primary-hover); +} + +.add-btn:hover { + background: var(--color-primary-hover); +} + +.item { + border-bottom: 1px solid var(--color-divider); + padding: 20px 0; +} + +.item:last-child { + border-bottom: none; +} + +.item.highlighted { + background: none; + margin: 0 -20px; + padding: 20px; + border-radius: 6px; + border: none; +} + +.item-row { + display: grid; + grid-template-columns: 1fr min-content 1fr; + align-items: center; +} + +.name, .name.strikethrough { + text-align: left; +} + +.name { + font-weight: 500; + font-size: 16px; + color: var(--color-text); +} + +.name.strikethrough { + text-decoration: line-through; + color: var(--color-text-disabled); +} + +.strikethrough { + text-decoration: line-through; + color: var(--color-text-disabled); +} + +.controls-container { + justify-self: center; + display: flex; + align-items: center; +} + +.controls { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; +} + +.qty, .qty-only { + width: 38px; + padding: 8px 0; + background: var(--color-bg-light); + border-radius: 6px; + font-size: 16px; + font-weight: 500; text-align: center; -} \ No newline at end of file + color: var(--color-text); +} + +.actions { + justify-self: end; + display: flex; + gap: 10px; + align-items: center; +} + +.circle-btn, +.buy-btn, +.buy-btn.inactive { + box-shadow: 0 3px 0 0 #bdbdbd; +} + +.circle-btn.minus, +.delete-btn { + box-shadow: 0 3px 0 0 var(--color-delete-shadow); +} + +.circle-btn.minus:disabled { + background-color: #ffb3b3; + color: white; + cursor: not-allowed; + box-shadow: 0 3px 0 0 #cc9999; + transform: none; +} + +.circle-btn.plus { + box-shadow: 0 3px 0 0 var(--color-plus-shadow); +} + +.circle-btn { + width: 36px; + height: 36px; + border-radius: 50%; + border: none; + font-size: 20px; + font-weight: bold; + color: white; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: transform 0.1s; +} + +.circle-btn:hover { + transform: scale(1.05); +} + +.circle-btn:active { + transform: scale(0.95); +} + +.minus { + background: var(--color-delete); +} + +.plus { + background: var(--color-plus); +} + +.editable-name { + max-width: 110px; + width: 100%; + border: 1px solid var(--color-border); + border-radius: 6px; + padding: 6px 12px; + font-size: 16px; + font-family: inherit; + box-sizing: border-box; + background: white; + color: var(--color-text); + outline: none; + transition: border-color 0.2s; +} + +.editable-name:focus { + border-color: var(--color-primary); +} + +.buy-btn, +.buy-btn.inactive { + background: var(--color-bg-light); + border: 1px solid var(--color-border); + padding: 8px 16px; + border-radius: 6px; + font-weight: 500; + font-size: 14px; + cursor: pointer; + color: var(--color-text); + transition: background-color 0.2s; + box-shadow: 0 3px 0 0 #c1c1c1; +} + +.buy-btn:hover { + background: var(--color-bg-light-hover); +} + +.buy-btn.inactive { + background: var(--color-bg-light); + color: var(--color-text-secondary); +} + +.delete-btn { + background: var(--color-delete); + color: white; + border: none; + padding: 8px 12px; + border-radius: 6px; + font-weight: bold; + font-size: 16px; + cursor: pointer; + transition: background-color 0.2s; + box-shadow: 0 3px 0 0 var(--color-delete-shadow); +} + +.delete-btn:hover { + background: var(--color-delete-hover); +} + +.section { + margin-bottom: 30px; +} + +.section:last-child { + margin-bottom: 0; +} + +.tags { + display: flex; + gap: 12px; + flex-wrap: wrap; +} + +.tag { + display: flex; + align-items: center; + background: var(--color-bg-light); + padding: 8px 12px; + border-radius: 20px; + color: var(--color-text); + font-weight: 500; + font-size: 14px; + gap: 8px; +} + +.tag.bought { + background: var(--color-bg-light); +} + +.tag-qty { + background: var(--color-qty); + color: white; + padding: 0; + border-radius: 50%; + font-weight: bold; + font-size: 14px; + min-width: 24px; + min-height: 24px; + border: 2px solid var(--color-qty); + box-sizing: border-box; + text-align: center; +} + +.divider { + border: none; + border-top: 1px solid var(--color-divider); + margin: 24px 0; +} + +.buy-badge { + position: fixed; + left: 2%; + bottom: 0; + background: var(--color-badge); + color: white; + font-size: 1.18rem; + font-weight: bold; + padding: 12px 18px 16px 18px; + border-radius: 10px 10px 0 0; + letter-spacing: 1px; + font-family: inherit; + cursor: pointer; + overflow: hidden; + text-align: center; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + box-sizing: border-box; + transition: all 0.8s ease-out; + height: auto; + min-height: 40px; +} + +.buy-badge:hover { + background: var(--color-badge-hover); +} + +.buy-badge-extra { + font-size: 0.95rem; + font-weight: normal; + color: white; + text-align: center; + margin-top: 6px; + width: 100%; + word-break: break-word; + opacity: 0; + max-height: 0; + pointer-events: none; + transition: all 1s ease-out; + line-height: 1.2; +} + +.buy-badge:hover .buy-badge-extra { + opacity: 1; + max-height: 100px; +} + +.buy-badge-main { + color: white; + width: 100%; + text-align: center; + line-height: 1.2; +} + +@media print { + .buy-badge { + background-color: white; + color: black; + border: 2px solid var(--color-badge); + box-shadow: none; + } + + .buy-badge-main { + display: none; + } + + .buy-badge-extra { + display: block; + opacity: 1; + color: black; + max-height: none; + } +} + +@media (max-width: 500px) { + .container { + flex-direction: column; + gap: 15px; + padding: 10px; + } +} + +@media (max-width: 700px) { + body { + padding: 10px; + } + + .container { + gap: 10px; + padding: 0 5px; + } + + .panel { + padding: 10px; + } + + .input-row { + flex-direction: column; + gap: 8px; + margin-bottom: 20px; + } + + input[type="text"] { + padding: 10px 12px; + font-size: 14px; + } + + .add-btn { + width: 100%; + padding: 10px 0; + font-size: 14px; + box-shadow: none; + } + + .item-row { + grid-template-columns: 1fr min-content 1fr; + gap: 5px; + } + + .name, .name.strikethrough { + font-size: 14px; + max-width: 100px; + word-break: break-word; + } + + .editable-name { + max-width: 100%; + font-size: 14px; + padding: 4px 8px; + } + + .circle-btn { + width: 30px; + height: 30px; + font-size: 18px; + } + + .qty, .qty-only { + width: 30px; + font-size: 14px; + padding: 6px 0; + } + + .actions { + gap: 6px; + } + + .buy-btn, + .buy-btn.inactive { + padding: 6px 12px; + font-size: 12px; + } + + .delete-btn { + padding: 6px 10px; + font-size: 14px; + } + + .buy-badge { + left: 1%; + padding: 8px 12px 12px 12px; + font-size: 1rem; + min-height: 36px; + } + + .buy-badge-extra { + font-size: 0.85rem; + } +} + + +[data-tooltip] { + position: relative; +} + +[data-tooltip]:before { + content: attr(data-tooltip); + position: absolute; + bottom: 100%; + left: 50%; + transform: translateX(-50%) translateY(10px) scale(0.8); + background: var(--color-badge); + color: white; + padding: 8px 12px; + border-radius: 8px; + font-size: 14px; + font-weight: 500; + white-space: nowrap; + opacity: 0; + pointer-events: none; + z-index: 1000; + margin-bottom: 8px; + transition: all 0.3s cubic-bezier(0.4, 0.0, 0.2, 1); + box-shadow: 0 4px 12px rgba(135, 23, 214, 0.3); +} + +[data-tooltip]:after { + content: ''; + position: absolute; + bottom: 100%; + left: 50%; + transform: translateX(-50%) translateY(5px) scale(0.8); + width: 0; + height: 0; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-top: 6px solid var(--color-badge); + opacity: 0; + pointer-events: none; + z-index: 1000; + margin-bottom: 2px; + transition: all 0.3s cubic-bezier(0.4, 0.0, 0.2, 1); +} + +[data-tooltip]:hover:before { + opacity: 1; + transform: translateX(-50%) translateY(0) scale(1); +} + +[data-tooltip]:hover:after { + opacity: 1; + transform: translateX(-50%) translateY(0) scale(1); +} diff --git a/script.js b/script.js new file mode 100644 index 0000000..6e2b7e1 --- /dev/null +++ b/script.js @@ -0,0 +1,263 @@ +const STORAGE_KEY = 'shoppingListItems'; +const NEXT_ID_KEY = 'shoppingListNextId'; + +const DEFAULT_ITEMS = [ + { id: 1, name: 'Помідори', quantity: 2, bought: true }, + { id: 2, name: 'Печиво', quantity: 3, bought: false }, + { id: 3, name: 'Сир', quantity: 1, bought: false } +]; + +let items = []; +let nextId = 1; +let editingItemId = null; + +function loadFromStorage() { + try { + const savedItems = localStorage.getItem(STORAGE_KEY); + const savedNextId = localStorage.getItem(NEXT_ID_KEY); + + if (savedItems && JSON.parse(savedItems).length > 0) { + items = JSON.parse(savedItems); + } else { + addDefaultItemsIfEmpty(); + return; + } + + if (savedNextId) { + nextId = parseInt(savedNextId, 10); + } else { + const maxId = items.reduce((max, item) => item.id > max ? item.id : max, 0); + nextId = maxId + 1; + saveToStorage(); + } + } catch (error) { + console.error('Помилка завантаження даних:', error); + addDefaultItemsIfEmpty(); + } +} + +function addDefaultItemsIfEmpty() { + if (items.length === 0) { + items = DEFAULT_ITEMS.map(item => ({ ...item })); + nextId = items.reduce((max, item) => item.id > max ? item.id : max, 0) + 1; + saveToStorage(); + } +} + +function saveToStorage() { + try { + localStorage.setItem(STORAGE_KEY, JSON.stringify(items)); + localStorage.setItem(NEXT_ID_KEY, nextId.toString()); + } catch (error) { + console.error('Помилка збереження даних:', error); + } +} + +function clearStorage() { + try { + localStorage.removeItem(STORAGE_KEY); + localStorage.removeItem(NEXT_ID_KEY); + } catch (error) { + console.error('Помилка очищення даних:', error); + } +} + +function generateId() { + return nextId++; +} + +function addItem(name) { + if (name.trim() === '') return; + + const newItem = { + id: generateId(), + name: name.trim(), + quantity: 1, + bought: false + }; + + items.push(newItem); + saveToStorage(); + renderItems(); + updateStats(); +} + +function deleteItem(id) { + items = items.filter(item => item.id !== id); + addDefaultItemsIfEmpty(); + saveToStorage(); + renderItems(); + updateStats(); +} + +function toggleBought(id) { + const item = items.find(item => item.id === id); + if (item) { + item.bought = !item.bought; + saveToStorage(); + renderItems(); + updateStats(); + } +} + +function updateQuantity(id, change) { + const item = items.find(item => item.id === id); + if (item) { + const newQuantity = item.quantity + change; + if (newQuantity >= 1) { + item.quantity = newQuantity; + saveToStorage(); + renderItems(); + updateStats(); + } + } +} + +function startEditing(id) { + if (editingItemId !== null) { + finishEditing(); + } + editingItemId = id; + renderItems(); + + setTimeout(() => { + const input = document.querySelector(`[data-item-id="${id}"] .editable-name`); + if (input) { + input.focus(); + input.select(); + } + }, 10); +} + +function finishEditing() { + if (editingItemId !== null) { + const input = document.querySelector(`[data-item-id="${editingItemId}"] .editable-name`); + if (input) { + const item = items.find(item => item.id === editingItemId); + if (item && input.value.trim() !== '') { + item.name = input.value.trim(); + saveToStorage(); + } + } + editingItemId = null; + renderItems(); + updateStats(); + } +} + +function clearAllItems() { + if (confirm('Ви впевнені, що хочете очистити весь список покупок?')) { + items = []; + nextId = 1; + addDefaultItemsIfEmpty(); // знову додати дефолтні + saveToStorage(); + renderItems(); + updateStats(); + } +} + +function renderItems() { + const itemsList = document.getElementById('itemsList'); + itemsList.innerHTML = ''; + + items.forEach(item => { + const itemDiv = document.createElement('div'); + itemDiv.className = 'item'; + itemDiv.setAttribute('data-item-id', item.id); + + const nameElement = editingItemId === item.id + ? `` + : `
${item.name}
`; + + const quantityControls = item.bought + ? `
${item.quantity}
` + : `
+ +
${item.quantity}
+ +
`; + + const actions = item.bought + ? `` + : ` + `; + + itemDiv.innerHTML = ` +
+ ${nameElement} +
+ ${quantityControls} +
+
+ ${actions} +
+
+ `; + + itemsList.appendChild(itemDiv); + }); +} + +function updateStats() { + const remainingTags = document.getElementById('remainingTags'); + const boughtTags = document.getElementById('boughtTags'); + + remainingTags.innerHTML = ''; + boughtTags.innerHTML = ''; + + const remainingItems = items.filter(item => !item.bought); + remainingItems.forEach(item => { + const tag = document.createElement('div'); + tag.className = 'tag'; + tag.innerHTML = ` + ${item.name} +
${item.quantity}
+ `; + remainingTags.appendChild(tag); + }); + + const boughtItems = items.filter(item => item.bought); + boughtItems.forEach(item => { + const tag = document.createElement('div'); + tag.className = 'tag bought'; + tag.innerHTML = ` + ${item.name} +
${item.quantity}
+ `; + boughtTags.appendChild(tag); + }); +} + +document.addEventListener('DOMContentLoaded', function() { + document.getElementById('addBtn').addEventListener('click', () => { + const input = document.getElementById('itemInput'); + addItem(input.value); + input.value = ''; + input.focus(); + }); + + document.getElementById('itemInput').addEventListener('keydown', (e) => { + if (e.key === 'Enter') { + const input = document.getElementById('itemInput'); + addItem(input.value); + input.value = ''; + } + }); + + document.addEventListener('click', (e) => { + if (editingItemId !== null && !e.target.closest('.editable-name') && !e.target.closest('.name')) { + finishEditing(); + } + }); + + window.addEventListener('beforeunload', () => { + if (editingItemId !== null) { + finishEditing(); + } + saveToStorage(); + }); + + loadFromStorage(); + renderItems(); + updateStats(); +});