Add undo button in toast for trash actions (single + batch)

This commit is contained in:
2026-05-12 19:40:23 +01:00
parent 617b8a7efe
commit ff9802e685
2 changed files with 13 additions and 5 deletions
+10 -5
View File
@@ -108,7 +108,7 @@ function toggleSound() {
}
// ==================== TOAST ====================
function toast(m, t) { t = t || 'success'; const c = document.getElementById('toastContainer'); const d = document.createElement('div'); d.className = 'toast ' + t; d.textContent = m; c.appendChild(d); setTimeout(() => d.remove(), 3000); }
function toast(m, t, action) { t = t || 'success'; const c = document.getElementById('toastContainer'); const d = document.createElement('div'); d.className = 'toast ' + t; d.innerHTML = '<span>' + m + '</span>'; if (action) { const btn = document.createElement('button'); btn.className = 'toast-action'; btn.textContent = action.label; btn.onclick = function(e) { e.stopPropagation(); action.cb(); d.remove(); }; d.appendChild(btn); c.appendChild(d); setTimeout(() => { if (!d.isConnected) return; d.remove(); if (action.onExpire) action.onExpire(); }, 6000); } else { c.appendChild(d); setTimeout(() => d.remove(), 3000); } }
function showZigzagToast(elem, msg, type) {
const t = document.createElement('div');
t.className = 'toast-zigzag ' + (type || 'success');
@@ -993,7 +993,7 @@ function showBatchConfirm(btn, message, callback) {
document.addEventListener('keydown', keyHandler);
}
function attachEvents() {
document.querySelectorAll('.delete-btn').forEach(b => b.onclick = function(ev) { ev.stopPropagation(); if (showTrash) { permanentDelete(this.dataset.id); } else { showConfirm(this, 'Delete this entry?', id => delEntry(id)); } });
document.querySelectorAll('.delete-btn').forEach(b => b.onclick = function(ev) { ev.stopPropagation(); const id = parseInt(this.dataset.id); if (showTrash) { permanentDelete(id); } else { showConfirm(this, 'Delete this entry?', async id2 => { await delEntry(id2); await loadEntries(); toast('📦 Moved to trash', 'success', { label: '↩ Undo', cb: async () => { await restoreEntry(id2, true); await loadEntries(); toast('↩ Restored'); playSound('success'); }, onExpire: null }); playSound('delete'); }); } });
document.querySelectorAll('.edit-btn').forEach(b => b.onclick = function(ev) { ev.stopPropagation(); openEdit(this.dataset.id); });
document.querySelectorAll('.star-btn').forEach(b => b.onclick = function(ev) { ev.stopPropagation(); toggleFavorite(this.dataset.id); });
document.querySelectorAll('.copy-p').forEach(b => b.onclick = async function(ev) { ev.stopPropagation(); const pw = entryPw(this.dataset.id); try { await navigator.clipboard.writeText(pw); this.textContent = '✓'; const btn = this; setTimeout(() => { btn.textContent = '📋'; }, 1000); showZigzagToast(this, '📋 Copied!', 'success'); playSound('copy'); } catch (e) { showZigzagToast(this, 'Failed', 'error'); } });
@@ -1140,13 +1140,18 @@ function hideBatchBar() {
async function batchDelete() {
const count = selectedIds.size;
const ids = [...selectedIds];
const btn = document.querySelector('#batchBar .btn-danger');
showBatchConfirm(btn || document.body, 'Move ' + count + ' entries to trash?', async () => {
for (const id of selectedIds) await delEntry(id, true);
toast('📦 Moved ' + count + ' entries to trash');
playSound('delete');
for (const id of ids) await delEntry(id, true);
clearSelection();
await loadEntries();
toast('📦 Moved ' + count + ' entries to trash', 'success', {
label: '↩ Undo',
cb: async () => { for (const id of ids) await restoreEntry(id, true); await loadEntries(); toast('↩ Restored ' + ids.length + ' entries'); playSound('success'); },
onExpire: null
});
playSound('delete');
});
}