diff --git a/public/components/modal.js b/public/components/modal.js index bbd7d66..7544985 100644 --- a/public/components/modal.js +++ b/public/components/modal.js @@ -294,6 +294,22 @@ export function openModal({ title, content, onSave, onDelete, size = 'md' } = {} // Callback für Aufrufer (Form-Events binden etc.) if (typeof onSave === 'function') onSave(panel); + // Loading-State: btn--loading auf Submit-Button während async-Save. + // rAF-Check: Validierung schlägt fehl → btn bleibt enabled → Loading sofort entfernen. + // MutationObserver: Error-Pfad → btn wird re-enabled → Loading entfernen. + panel.addEventListener('submit', (e) => { + const btn = e.target.querySelector('[type="submit"], .btn--primary'); + if (!btn || btn.disabled) return; + btn.classList.add('btn--loading'); + requestAnimationFrame(() => { + if (!btn.disabled) { btn.classList.remove('btn--loading'); return; } + const mo = new MutationObserver(() => { + if (!btn.disabled) { btn.classList.remove('btn--loading'); mo.disconnect(); } + }); + mo.observe(btn, { attributes: true, attributeFilter: ['disabled'] }); + }); + }, { capture: true }); + // Standalone: Statusbar abdunkeln (Overlay-Effekt) if (window.oikos?.setThemeColor) { window.oikos.setThemeColor(OVERLAY_THEME_COLOR, OVERLAY_THEME_COLOR); @@ -620,6 +636,7 @@ export function validateAll(formContainer) { * @param {string} [originalLabel] */ export function btnSuccess(btn, originalLabel) { + btn.classList.remove('btn--loading'); const label = originalLabel ?? btn.textContent; btn.classList.add('btn--success'); const reducedMotion = matchMedia('(prefers-reduced-motion: reduce)').matches; diff --git a/public/styles/layout.css b/public/styles/layout.css index 87faf07..1102fd9 100755 --- a/public/styles/layout.css +++ b/public/styles/layout.css @@ -1647,10 +1647,20 @@ opacity: 0.85; } +.toast__undo { + -webkit-tap-highlight-color: transparent; + transition: opacity var(--transition-fast), transform 0.08s ease; +} + .toast__undo:hover { opacity: 1; } +.toast__undo:active { + transform: scale(0.94); + opacity: 1; +} + .toast--success { background-color: var(--color-success); color: var(--toast-success-text); } .toast--danger { background-color: var(--color-danger); color: var(--toast-danger-text); } .toast--warning { background-color: var(--color-warning); color: var(--toast-warning-text); } diff --git a/public/styles/tasks.css b/public/styles/tasks.css index 46418c0..663625e 100644 --- a/public/styles/tasks.css +++ b/public/styles/tasks.css @@ -406,11 +406,16 @@ color: var(--color-text-primary); margin-bottom: var(--space-1); cursor: pointer; + text-decoration: line-through; + text-decoration-color: transparent; + transition: + color var(--transition-fast), + text-decoration-color var(--transition-base); } .task-card--done .task-card__title { - text-decoration: line-through; color: var(--color-text-secondary); + text-decoration-color: var(--color-text-secondary); } .task-card__meta {