I have a Rails 7 web app that is using Tailwind CSS, Stimulus.js, and animate.css. I have flash messages setup in Rails and I'm trying to add a fadeIn and FadeOut animation. The message will appear for 5 seconds and then will disappear, it also has a button where the user can dismiss the message immediately.
The fadeIn is working after I added class="animate__animated animate__fadeIn" however, I don't know how to get the fadeOut to work correctly when the button is pressed or when the message disappears after 5 seconds.
<% flash.each do |message_type, message| %>
<div data-controller="flash" class="animate__animated animate__fadeIn">
<div class="mt-5 mb-15 sm:mx-auto sm:w-full sm:max-w-md px-2">
<div class="rounded-md bg-red-50 p-4">
<div class="flex">
<div class="flex-shrink-0">
<!-- Heroicon name: solid/x-circle -->
<svg class="h-5 w-5 text-red-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" />
</svg>
</div>
<div class="ml-3">
<h3 class="text-sm font-medium text-red-800"><%= message %></h3>
</div>
<div class="ml-auto pl-3">
<div class="-mx-1.5 -my-1.5">
<button type="button" data-action="flash#dismiss" class="inline-flex bg-red-50 rounded-md p-1.5 text-red-500 hover:bg-red-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-red-50 focus:ring-red-600">
<span class="sr-only">Dismiss</span>
<!-- Heroicon name: solid/x -->
<svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<% end %>
flash_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
connect() {
setTimeout(() => {
this.dismiss();
}, 5000);
}
dismiss() {
// document.getElementById("flash").className = document.getElementById("flash").className.replace(/(?:^|\s)animate__fadeIn(?!\S)/g, 'animate__fadeOut');
// const div = this.element.querySelector('animate__fadeIn');
// div.classList.replace('animate__fadeIn','animate__fadeOut');
// this.element.classList.remove(animate__fadeIn);
// this.element.classList.add(animate__fadeOut);
this.element.remove();
}
}
Animate.css documentation provides a very solid solution to this kind of problem in its javascript section - https://animate.style/#javascript
The approach is to create a reusable function that allows for a callback (in the form of a Promise) to run after an animation has completed.
Below is an example of your code with this function, but all credit codes to the Animate.css documentation for the underlying code.
Example
hiddenattribute (note: this could be a tailwind class also) to hide by default.connectto trigger the fadeIn animation, and fadeOut in thedismissmethod.