fix(ux): replace native confirm() dialogs, add undo-toast, fix prefers-reduced-motion

- Replace all 13 native confirm() calls with confirmModal() across 7 page modules
- Add confirmModal() to modal.js (Promise-based, danger variant, focus management)
- Fix double-confirm bug in contacts.js and budget.js (modal + deleteContact/deleteEntry)
- Extend showToast() with onUndo callback and max-3-toast limit
- Implement optimistic undo-toast (4s window) for shopping item and bulk-checked delete
- Add prefers-reduced-motion guard to btnSuccess() and btnError() in modal.js
- Add btn--error-static CSS class as motion-reduced fallback for btnError()
- Add toast__undo button styles to layout.css
- Add common.confirm and common.undo i18n keys (de, en, it, sv)
- Add shopping.itemDeletedToast i18n key (de, en, it, sv)
This commit is contained in:
Ulas
2026-04-05 12:31:16 +02:00
parent 3a7d6d0e0a
commit 44e5a879b9
15 changed files with 245 additions and 56 deletions
+3 -3
View File
@@ -6,7 +6,7 @@
import { api } from '/api.js';
import { renderRRuleFields, bindRRuleEvents, getRRuleValues } from '/rrule-ui.js';
import { openModal as openSharedModal, closeModal } from '/components/modal.js';
import { openModal as openSharedModal, closeModal, confirmModal } from '/components/modal.js';
import { stagger } from '/utils/ux.js';
import { t, formatTime } from '/i18n.js';
import { esc } from '/utils/html.js';
@@ -701,7 +701,7 @@ function showEventPopup(ev, anchor) {
});
popup.querySelector('#popup-delete').addEventListener('click', async () => {
if (!confirm(t('calendar.deleteConfirm', { title: ev.title }))) return;
if (!await confirmModal(t('calendar.deleteConfirm', { title: ev.title }), { danger: true, confirmLabel: t('common.delete') })) return;
popup.remove();
await deleteEvent(ev.id);
});
@@ -759,7 +759,7 @@ function openEventModal({ mode, event = null, date = null }) {
panel.querySelector('#modal-cancel').addEventListener('click', closeModal);
panel.querySelector('#modal-delete')?.addEventListener('click', async () => {
if (!confirm(t('calendar.deleteConfirm', { title: event.title }))) return;
if (!await confirmModal(t('calendar.deleteConfirm', { title: event.title }), { danger: true, confirmLabel: t('common.delete') })) return;
closeModal();
await deleteEvent(event.id);
});