timolins / react-hot-toast

Smoking Hot React Notifications 🔥
https://react-hot-toast.com
MIT License
9.81k stars 331 forks source link

toast.custom but with animation #116

Open seanonthenet opened 3 years ago

seanonthenet commented 3 years ago

Hi. Is there a way I can use toast.custom but include the standard animation? I'm wanting to use Tailwind UI components for the toast but use the default react-hot-toast display and disappear animations.

AsadSaleh commented 3 years ago

Yeah, I'm having the same issue.

I think the solution is for us to implement "animate-enter" and 'animate-leave" in our tailwind configuration. But if the author could provide the original solution, that would be great.

AsadSaleh commented 3 years ago

Update:

We can see the tailwind.config.js here

However, even though I applied both animations to my code, only animation-enter that's working nicely. For unknown reason the "leaving animation" didn't get triggered and it just vanished when I clicked the close button.

Anyone can help with the exit animation using Tailwind?

Thanks in advance.

shekhar-shubhendu commented 2 years ago

Same issue. I'm using this framer-motion example but exit animation doesn't play.

hi-reeve commented 2 years ago

got the same issues with framer on exit animation

LuisValgoi commented 9 months ago

for anyone looking for a solution that does not uses tailwindcss...I was able to make it work using the following:

styling

@keyframes customerEnter {
  0% {
    transform: translateY(-100%);
    opacity: 0;
  }
  100% {
    transform: translateY(0);
    opacity: 1;
  }
}

@keyframes customLeave {
  0% {
    transform: translateY(0);
    opacity: 1;
  }
  100% {
    transform: translateY(-100%);
    opacity: 0;
  }
}

.toaster {
  position: relative;
  width: auto;
}

.animateEnter {
  animation: customerEnter 0.2s ease-out;
}

.animateLeave {
  animation: customLeave 0.2s ease-out;
}

definition

export function useToast() {
  const { t } = useTranslation();

  const Toast = () => {
    const handleDismiss = () => toast.remove();

    return (
      <div
        className={classNames(
          styles.toaster,
          { [styles.animateEnter]: visible },
          { [styles.animateLeave]: !visible }
        )}
      >
        <button onClick={handleDismiss}>close</button>
        <p>message</p>
      </div>
    );
  };

  return {
    show: () =>
      toast.custom((t) => <Toast visible={t.visible} {...props} />),
  };
}

usage

const { show } = useToast();
...
onClick={show}
...
szymonkadas commented 3 months ago

Above solution didn't work for me, so i've decided to use some native js inside useEffect, and it works! I tried to use classes firsthand, but it was buggy, though maybe you'll be able to make it work, i've tried with element.classList.add, remove.

Notification function using toast:

interface NotificationProps {
  title: string;
  message: string;
  id: string;
  duration?: number;
}

export default function Notification({
  title,
  message,
  id,
  duration = 4000
}: NotificationProps) {
  toast.remove(id);
  toast.custom(
      <NotificationElement
        title={title}
        id={id}
        message={message}
        duration={duration}
      />,
    { id, position: 'top-right', duration: duration }
  );
}

NotificationElement (component) with useEffect:

const NotificationElement = ({
  title,
  message,
  id,
  duration = 2000
}: NotificationProps) => {
  // handling disappear animation due to react-hot-toast limitations with custom toasts for exit anim - 08.08.2024
  useEffect(() => {
    const timeoutFireTime = Math.max(duration - 200, 0);
    const exitTimeout= setTimeout(() => {
      const element = document.getElementById(id);
      if (element) {
        // classes would work aswell, though would be buggy if spammed with same id notifications.
        element.style.animation = 'disappear 0.2s ease-in-out forwards';
      }
    }, timeoutFireTime);

    return ()=>{
      clearTimeout(exitTimeout);
      const element = document.getElementById(id);
      if(element){
        element.style.animation = 'appear 0.2s ease-in-out forwards';
      }
    }
  }, );

  return (
    <div
      className={`appear flex flex-col gap-1.5 rounded-lg border-2 border-main-accent 
      bg-main-background px-6 py-2 text-main-primary`}
      id={id}
    >
      <div className='flex items-start justify-between gap-8 text-xl'>
        <h3 className='size-fit'>{title}</h3>
        <button
          className='button-hover-background -my-2 -ml-4 -mr-6 rounded border-0 bg-transparent px-4 py-2 text-xl text-main-accent'
          onClick={() => toast.remove(id)}
        >
          &#10006;
        </button>
      </div>
      <p className='text-base'>{message}</p>
    </div>
  );
};

appear and disappear are my animations, feel free to use your own.