From e45e742850716ba9889a01acf23fa0be4130fc62 Mon Sep 17 00:00:00 2001
From: Alex Bueno <44420072+aweell@users.noreply.github.com>
Date: Tue, 19 Nov 2024 16:55:06 +0100
Subject: [PATCH] Fix toast issues

---
 .../components/toast-wrapper.jsx              | 37 +++++++----
 .../advent-calendar-2024/components/toast.jsx | 63 ++++++++++++-------
 .../components/toast.module.css               | 23 +++++++
 3 files changed, 87 insertions(+), 36 deletions(-)
 create mode 100644 src/pages/advent-calendar-2024/components/toast.module.css

diff --git a/src/pages/advent-calendar-2024/components/toast-wrapper.jsx b/src/pages/advent-calendar-2024/components/toast-wrapper.jsx
index 7e5935bd6a..f9c502e355 100644
--- a/src/pages/advent-calendar-2024/components/toast-wrapper.jsx
+++ b/src/pages/advent-calendar-2024/components/toast-wrapper.jsx
@@ -1,7 +1,10 @@
 import { useState, useEffect } from "react";
 import Toast from "./toast"; // Assuming your Toast component is already implemented
+import { Stack } from "@telefonica/mistica";
 
 const ToastWrapper = ({ toasts, removeToast }) => {
+  const totalToasts = toasts.length;
+
   return (
     <div
       style={{
@@ -10,18 +13,28 @@ const ToastWrapper = ({ toasts, removeToast }) => {
         right: "16px",
       }}
     >
-      <div style={{ position: "relative" }}>
-        {toasts.map((toast, index) => (
-          <Toast
-            id={toast.id}
-            key={toast.id}
-            title={toast.name}
-            description={toast.message}
-            icon={toast.icon}
-            onClose={() => removeToast(toast.id)} // Dismiss the toast by `id`
-          />
-        ))}
-      </div>
+      <Stack space={8}>
+        {toasts.map((toast, index) => {
+          const scaleValue = 1 - index * 0.2;
+          console.log(`scale(${scaleValue})`); // Log the scale value
+
+          return (
+            <Toast
+              id={toast.id}
+              key={toast.id}
+              title={toast.name}
+              description={toast.message}
+              icon={toast.icon}
+              onClose={() => removeToast(toast.id)} // Dismiss the toast by `id`
+              style={{
+                right: "16px",
+                zIndex: 1000 + index,
+              }}
+              delay={(totalToasts - index - 1) * 1000} // Last toast will have the longest delay
+            />
+          );
+        })}
+      </Stack>
     </div>
   );
 };
diff --git a/src/pages/advent-calendar-2024/components/toast.jsx b/src/pages/advent-calendar-2024/components/toast.jsx
index 0c1353ba87..69b2d96722 100644
--- a/src/pages/advent-calendar-2024/components/toast.jsx
+++ b/src/pages/advent-calendar-2024/components/toast.jsx
@@ -1,4 +1,4 @@
-import { useEffect, useState, useRef } from "react";
+import { useState, useEffect, useRef } from "react";
 import {
   Stack,
   Text3,
@@ -9,16 +9,19 @@ import {
   IconButton,
   IconCloseRegular,
 } from "@telefonica/mistica";
+import styles from "./toast.module.css";
 
 const Toast = ({
   title,
   description,
   icon: Icon,
   duration = 3000,
-  style,
+  delay = 0,
   onClose,
+  style,
 }) => {
-  const [visible, setVisible] = useState(true);
+  const [visible, setVisible] = useState(true); // Initial visibility is true
+  const [fadeOut, setFadeOut] = useState(false); // Controls fade-out animation
   const [isHovered, setIsHovered] = useState(false);
   const timeoutRef = useRef(null);
 
@@ -32,44 +35,53 @@ const Toast = ({
   const startHideTimeout = () => {
     clearHideTimeout();
     timeoutRef.current = setTimeout(() => {
-      setVisible(false); // This will trigger unmount if necessary
-      onClose?.(); // Pass the ID to remove the toast
+      setFadeOut(true); // Trigger fade-out before setting visible to false
+      onClose?.(); // Trigger onClose callback to remove toast
     }, duration);
   };
 
+  // Handle visibility change when hovered or not
   useEffect(() => {
-    if (!isHovered) {
-      startHideTimeout(); // Start timeout when not hovered
-    } else {
-      clearHideTimeout(); // Clear timeout when hovered
-    }
+    const handleTimeout = () => {
+      if (!isHovered) {
+        setTimeout(startHideTimeout, delay); // Delay before auto-dismissing
+      } else {
+        clearHideTimeout(); // Clear timeout when hovered
+      }
+    };
+
+    handleTimeout();
 
     return () => clearHideTimeout(); // Cleanup timeout on unmount
-  }, [isHovered, duration]);
+  }, [isHovered, delay, duration]);
 
-  // Always render dismiss button, regardless of toast visibility
-  const handleDismiss = () => {
-    setVisible(false);
-    clearHideTimeout();
-    onClose?.(); // Ensure it triggers onClose from parent
-  };
+  useEffect(() => {
+    if (fadeOut) {
+      // Wait for the exit animation to complete before removing the toast
+      timeoutRef.current = setTimeout(() => {
+        setVisible(false); // Remove from the DOM after animation
+      }, 500); // Match the duration of your fade-out transition
+    }
+
+    return () => clearTimeout(timeoutRef.current); // Cleanup the fade-out timeout
+  }, [fadeOut]);
 
-  if (!visible) return null; // Hide toast when not visible
+  if (!visible) return null; // If not visible, do not render the toast
 
   return (
     <div
+      className={`${styles.toast} ${fadeOut ? styles.exit : ""}`} // Add the exit class when fadeOut is true
       style={{
         background: skinVars.colors.background,
         padding: "24px",
         borderRadius: "8px",
         border: `2px solid ${skinVars.colors.border}`,
         zIndex: 1000,
-        width: 480,
-        position: "relative", // Ensures dismiss button is on top
+        width: "480px",
         ...style,
       }}
-      onMouseEnter={() => setIsHovered(true)}
-      onMouseLeave={() => setIsHovered(false)}
+      onMouseEnter={() => setIsHovered(true)} // Trigger hover state
+      onMouseLeave={() => setIsHovered(false)} // Reset hover state
     >
       <Inline space={16}>
         <div
@@ -96,11 +108,14 @@ const Toast = ({
             View progress
           </ButtonLink>
         </Stack>
-        {/* Dismiss button should always be on top */}
         <div style={{ position: "absolute", top: 8, right: 8 }}>
           <IconButton
             Icon={IconCloseRegular}
-            onPress={handleDismiss} // Handle dismiss in the component itself
+            onPress={() => {
+              setFadeOut(true); // Trigger fade-out animation on close
+              clearHideTimeout(); // Clear the timeout when manually closed
+              onClose?.(); // Trigger onClose callback to remove toast
+            }}
           />
         </div>
       </Inline>
diff --git a/src/pages/advent-calendar-2024/components/toast.module.css b/src/pages/advent-calendar-2024/components/toast.module.css
new file mode 100644
index 0000000000..1e0746c125
--- /dev/null
+++ b/src/pages/advent-calendar-2024/components/toast.module.css
@@ -0,0 +1,23 @@
+/* toast.module.css */
+
+/* Keyframes for the toast entry animation */
+@keyframes slideIn {
+  0% {
+    opacity: 0;
+    transform: translateY(400px); /* Start below */
+  }
+  100% {
+    opacity: 1; /* Fade in */
+    transform: translateY(0); /* Move to final position */
+  }
+}
+
+
+
+/* Base toast styles */
+.toast {
+  opacity: 0;
+  transform: translateY(400px); /* Initial off-screen position */
+  animation: slideIn 0.5s ease-out forwards; /* Entry animation */
+}
+