Add undo button in toast for trash actions (single + batch)
This commit is contained in:
@@ -25,6 +25,9 @@ body {
|
||||
.toast { padding:0.7rem 1.2rem; border-radius:0.8rem; font-size:0.85rem; animation:slideIn 0.3s ease; color:#fff; }
|
||||
.toast.error { background:var(--danger); }
|
||||
.toast.success { background:var(--success); }
|
||||
.toast { display:flex; align-items:center; gap:0.6rem; }
|
||||
.toast-action { background:rgba(255,255,255,0.25); border:none; color:#fff; font-weight:700; font-size:0.75rem; padding:0.25rem 0.6rem; border-radius:1rem; cursor:pointer; white-space:nowrap; transition:0.15s; }
|
||||
.toast-action:hover { background:rgba(255,255,255,0.4); }
|
||||
@keyframes slideIn { from { transform:translateX(100%); opacity:0; } to { transform:translateX(0); opacity:1; } }
|
||||
|
||||
.vault {
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user