fix(toast): enable proper exit animations (#5121)

* fix(toast): enable proper exit animations
	•	Use LazyMotion and AnimatePresence in ToastProvider to support exit animations.
	•	Simplify Toast by removing redundant LazyMotion wrapper.
	•	Add motionProps to stories for easier animation customization.

* chore(changeset): add changeset for ToastProvider exit animations

* chore(toast): clean up stories by removing motionProps argument

* chore(docs): revert `CONTRIBUTING.md` and `toast.stories.tsx`to initial state
This commit is contained in:
Alexander Gavrilenko 2025-03-28 12:40:57 +01:00 committed by GitHub
parent 0cc127b1c7
commit ca5babcbb9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 35 additions and 31 deletions

View File

@ -0,0 +1,5 @@
---
"@heroui/toast": patch
---
- Use LazyMotion and AnimatePresence in ToastProvider to support exit animations.

View File

@ -1,9 +1,12 @@
import {ToastOptions, ToastQueue, useToastQueue} from "@react-stately/toast";
import {useProviderContext} from "@heroui/system";
import {AnimatePresence, LazyMotion} from "framer-motion";
import {RegionProps, ToastRegion} from "./toast-region";
import {ToastProps, ToastPlacement} from "./use-toast";
const loadFeatures = () => import("framer-motion").then((res) => res.domMax);
let globalToastQueue: ToastQueue<ToastProps> | null = null;
interface ToastProviderProps {
@ -37,20 +40,22 @@ export const ToastProvider = ({
const globalContext = useProviderContext();
const disableAnimation = disableAnimationProp ?? globalContext?.disableAnimation ?? false;
if (toastQueue.visibleToasts.length == 0) {
return null;
}
return (
<ToastRegion
disableAnimation={disableAnimation}
maxVisibleToasts={maxVisibleToasts}
placement={placement}
toastOffset={toastOffset}
toastProps={toastProps}
toastQueue={toastQueue}
{...regionProps}
/>
<LazyMotion features={loadFeatures}>
<AnimatePresence>
{toastQueue.visibleToasts.length > 0 ? (
<ToastRegion
disableAnimation={disableAnimation}
maxVisibleToasts={maxVisibleToasts}
placement={placement}
toastOffset={toastOffset}
toastProps={toastProps}
toastQueue={toastQueue}
{...regionProps}
/>
) : null}
</AnimatePresence>
</LazyMotion>
);
};

View File

@ -7,14 +7,12 @@ import {
SuccessIcon,
WarningIcon,
} from "@heroui/shared-icons";
import {AnimatePresence, m, LazyMotion} from "framer-motion";
import {m} from "framer-motion";
import {cloneElement, isValidElement} from "react";
import {Spinner} from "@heroui/spinner";
import {UseToastProps, useToast} from "./use-toast";
const loadFeatures = () => import("framer-motion").then((res) => res.domMax);
export interface ToastProps extends UseToastProps {}
const iconMap = {
@ -108,21 +106,17 @@ const Toast = forwardRef<"div", ToastProps>((props, ref) => {
{disableAnimation ? (
toastContent
) : (
<LazyMotion features={loadFeatures}>
<AnimatePresence>
<m.div {...getMotionDivProps()}>
<m.div
key={"inner-div"}
animate={{opacity: 1}}
exit={{opacity: 0}}
initial={{opacity: 0}}
transition={{duration: 0.25, ease: "easeOut", delay: 0.1}}
>
{toastContent}
</m.div>
</m.div>
</AnimatePresence>
</LazyMotion>
<m.div {...getMotionDivProps()}>
<m.div
key={"inner-div"}
animate={{opacity: 1}}
exit={{opacity: 0}}
initial={{opacity: 0}}
transition={{duration: 0.25, ease: "easeOut", delay: 0.1}}
>
{toastContent}
</m.div>
</m.div>
)}
</>
);