Full document, spreadsheet, slideshow, and diagram tooling
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request 'feat: toast notification with undo for document deletion' (#99) from feat/delete-confirm-toast into main

scott 7b1d3880 e9c4972c

+45 -2
+13
src/css/app.css
··· 2718 2718 .toast-notification.toast-error { 2719 2719 background: var(--color-danger); 2720 2720 } 2721 + .toast-notification.toast-interactive { 2722 + pointer-events: auto; 2723 + } 2724 + .toast-undo { 2725 + font-weight: 700; 2726 + text-decoration: underline; 2727 + cursor: pointer; 2728 + margin-left: 0.75rem; 2729 + opacity: 0.9; 2730 + } 2731 + .toast-undo:hover { 2732 + opacity: 1; 2733 + } 2721 2734 2722 2735 /* --- Drag-and-drop import overlay --- */ 2723 2736 .drop-overlay {
+32 -2
src/landing.ts
··· 405 405 trash = addToTrash(trash, id); 406 406 localStorage.setItem('tools-trash', JSON.stringify(trash)); 407 407 renderDocuments(); 408 + showToast('Document moved to trash', 5000, false, () => { 409 + trash = restoreFromTrash(trash, id); 410 + localStorage.setItem('tools-trash', JSON.stringify(trash)); 411 + renderDocuments(); 412 + }); 408 413 }); 409 414 }); 410 415 ··· 659 664 dropOverlay.style.display = 'none'; 660 665 } 661 666 662 - function showToast(message: string, duration = 3000, isError = false): void { 667 + function showToast(message: string, duration = 3000, isError = false, onUndo?: () => void): void { 663 668 const existing = document.querySelector('.toast-notification'); 664 669 if (existing) existing.remove(); 665 670 const toast = document.createElement('div'); 666 671 toast.className = 'toast-notification' + (isError ? ' toast-error' : ''); 667 - toast.textContent = message; 672 + if (onUndo) { 673 + toast.classList.add('toast-interactive'); 674 + const msgSpan = document.createElement('span'); 675 + msgSpan.textContent = message; 676 + toast.appendChild(msgSpan); 677 + const undoBtn = document.createElement('span'); 678 + undoBtn.className = 'toast-undo'; 679 + undoBtn.textContent = 'Undo'; 680 + undoBtn.setAttribute('role', 'button'); 681 + undoBtn.setAttribute('tabindex', '0'); 682 + undoBtn.addEventListener('click', () => { 683 + onUndo(); 684 + toast.classList.remove('toast-visible'); 685 + setTimeout(() => toast.remove(), 300); 686 + }); 687 + undoBtn.addEventListener('keydown', (e) => { 688 + if (e.key === 'Enter' || e.key === ' ') { 689 + e.preventDefault(); 690 + undoBtn.click(); 691 + } 692 + }); 693 + toast.appendChild(undoBtn); 694 + duration = 5000; 695 + } else { 696 + toast.textContent = message; 697 + } 668 698 document.body.appendChild(toast); 669 699 toast.offsetHeight; // force reflow 670 700 toast.classList.add('toast-visible');