Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix toast issues #1966

Merged
merged 1 commit into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 25 additions & 12 deletions src/pages/advent-calendar-2024/components/toast-wrapper.jsx
Original file line number Diff line number Diff line change
@@ -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={{
Expand All @@ -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>
);
};
Expand Down
63 changes: 39 additions & 24 deletions src/pages/advent-calendar-2024/components/toast.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useState, useRef } from "react";
import { useState, useEffect, useRef } from "react";
import {
Stack,
Text3,
Expand All @@ -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);

Expand All @@ -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
Expand All @@ -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>
Expand Down
23 changes: 23 additions & 0 deletions src/pages/advent-calendar-2024/components/toast.module.css
Original file line number Diff line number Diff line change
@@ -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 */
}

Loading