feat: blur-triggered inline validation and submit button feedback

Task 13: wireBlurValidation() activates error/valid state on required
fields after blur. Task 14: btnSuccess() shows a checkmark for 700ms
then closes the modal; btnError() triggers a shake animation on failure.
Both wired into the tasks form submit handler.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ulas
2026-03-30 21:22:36 +02:00
parent 1684215da8
commit 0ac2769fac
3 changed files with 125 additions and 7 deletions
+54
View File
@@ -269,3 +269,57 @@ export function closeModal() {
_doClose();
}
// --------------------------------------------------------
// Inline Blur-Validierung
// --------------------------------------------------------
/**
* Aktiviert Blur-Validierung für alle required-Inputs in einem Container.
* @param {HTMLElement} formContainer
*/
export function wireBlurValidation(formContainer) {
formContainer.querySelectorAll('input[required], select[required], textarea[required]').forEach((input) => {
input.addEventListener('blur', () => {
const group = input.closest('.form-field') ?? input.parentElement;
const hasValue = input.value.trim().length > 0;
group?.classList.toggle('form-field--error', !hasValue);
group?.classList.toggle('form-field--valid', hasValue);
});
});
}
// --------------------------------------------------------
// Submit-Feedback (Checkmark + Shake)
// --------------------------------------------------------
/**
* Zeigt Erfolgs-Feedback auf einem Button (Checkmark für 700ms).
* @param {HTMLButtonElement} btn
* @param {string} [originalLabel]
*/
export function btnSuccess(btn, originalLabel) {
const label = originalLabel ?? btn.textContent;
btn.classList.add('btn--success');
btn.innerHTML = `
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2.5" aria-hidden="true">
<polyline points="20 6 9 17 4 12"/>
</svg>
`;
setTimeout(() => {
btn.classList.remove('btn--success');
btn.textContent = label;
}, 700);
}
/**
* Zeigt Fehler-Feedback auf einem Button (Shake-Animation).
* @param {HTMLButtonElement} btn
*/
export function btnError(btn) {
btn.classList.remove('btn--shaking');
void btn.offsetWidth; // Reflow für Animation-Restart
btn.classList.add('btn--shaking');
btn.addEventListener('animationend', () => btn.classList.remove('btn--shaking'), { once: true });
}