Skip to content

Commit

Permalink
chore: refactoring and refining the animations
Browse files Browse the repository at this point in the history
  • Loading branch information
macci001 committed Dec 27, 2024
1 parent a36b58a commit 73fd38d
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 57 deletions.
1 change: 1 addition & 0 deletions packages/components/toast/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"@react-aria/button": "3.9.5",
"@react-aria/toast": "3.0.0-beta.15",
"@react-aria/utils": "3.24.1",
"@react-aria/interactions": "3.24.1",
"@react-stately/toast": "3.0.0-beta.5"
},
"devDependencies": {
Expand Down
46 changes: 23 additions & 23 deletions packages/components/toast/src/toast-region.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import {useToastRegion, AriaToastRegionProps} from "@react-aria/toast";
import {QueuedToast, ToastState} from "@react-stately/toast";
import {createPortal} from "react-dom";
import {clsx} from "@nextui-org/shared-utils";
import {useHover} from "@react-aria/interactions";
import {mergeProps} from "@react-aria/utils";

import Toast from "./toast";
import {ToastProps} from "./use-toast";
Expand All @@ -25,39 +27,37 @@ export function ToastRegion<T extends ToastProps>({
}: ToastRegionProps<T>) {
const ref = useRef(null);
const {regionProps} = useToastRegion(props, toastQueue, ref);
let positionStyle;

switch (position) {
case "right-bottom":
positionStyle = "bottom-0 right-0 pr-2";
break;
case "left-bottom":
positionStyle = "bottom-0 left-0 pl-2";
break;
case "center-bottom":
positionStyle = "bottom-0 left-1/2 -translate-x-1/2";
break;
case "right-top":
positionStyle = "top-0 right-0 pr-2";
break;
case "left-top":
positionStyle = "top-0 left-0 pl-2";
break;
case "center-top":
positionStyle = "top-0 left-1/2 -translate-x-1/2";
break;
}
const positionStyles: Record<string, string> = {
"right-bottom": "bottom-0 right-0 pr-2",
"left-bottom": "bottom-0 left-0 pl-2",
"center-bottom": "bottom-0 left-1/2 -translate-x-1/2",
"right-top": "top-0 right-0 pr-2",
"left-top": "top-0 left-0 pl-2",
"center-top": "top-0 left-1/2 -translate-x-1/2",
};
const positionStyle = position ? positionStyles[position] : positionStyles["right-bottom"];
const {hoverProps, isHovered} = useHover({
isDisabled: false,
});

return createPortal(
<div {...regionProps} ref={ref} className={clsx("fixed flex flex-col", positionStyle)}>
{toastQueue.visibleToasts.map((toast: QueuedToast<ToastProps>) => {
<div
{...mergeProps(regionProps, hoverProps)}
ref={ref}
className={clsx("fixed flex flex-col", positionStyle)}
>
{toastQueue.visibleToasts.map((toast: QueuedToast<ToastProps>, index) => {
return (
<Toast
key={toast.key}
state={toastQueue}
toast={toast}
{...toast.content}
index={index}
isRegionHovered={isHovered}
position={position}
total={toastQueue.visibleToasts.length}
/>
);
})}
Expand Down
68 changes: 36 additions & 32 deletions packages/components/toast/src/toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
WarningIcon,
} from "@nextui-org/shared-icons";
import {motion, AnimatePresence} from "framer-motion";
import {cloneElement, isValidElement, useState} from "react";
import {cloneElement, isValidElement, useEffect, useState} from "react";

import {UseToastProps, useToast} from "./use-toast";

Expand Down Expand Up @@ -38,6 +38,9 @@ const Toast = forwardRef<"div", ToastProps>((props, ref) => {
classNames,
slots,
isProgressBarVisible,
total,
index,
isRegionHovered,
getToastProps,
getContentProps,
getTitleProps,
Expand All @@ -49,6 +52,16 @@ const Toast = forwardRef<"div", ToastProps>((props, ref) => {
ref,
});

const [toastHeight, setToastHeight] = useState(0);

useEffect(() => {
if (domRef.current) {
const {height} = domRef.current.getBoundingClientRect();

setToastHeight(height);
}
}, []);

const toastVariants = position.includes("bottom")
? {
hidden: {opacity: 0, y: 50},
Expand All @@ -63,35 +76,19 @@ const Toast = forwardRef<"div", ToastProps>((props, ref) => {

const customIcon = icon && isValidElement(icon) ? cloneElement(icon, getIconProps()) : null;
const IconComponent = iconMap[color] || iconMap.primary;
const [isOut, setIsOut] = useState(false);

const handleDragEnd = (offsetX: number, offsetY: number) => {
if (position.includes("right")) {
if (offsetX < 50) {
return;
}
setIsOut(true);
state.close(toast.key);
}
if (position.includes("left")) {
if (offsetX > -50) {
return;
}
setIsOut(true);
state.close(toast.key);
}
if (position == "center-top") {
if (offsetY > -50) {
return;
}
setIsOut(true);
state.close(toast.key);
}
if (position == "center-bottom") {
if (offsetY < 50) {
return;
}
setIsOut(true);
const isRight = position.includes("right");
const isLeft = position.includes("left");
const isTop = position === "center-top";
const isBottom = position === "center-bottom";

if (
(isRight && offsetX >= 50) ||
(isLeft && offsetX <= -50) ||
(isTop && offsetY <= -50) ||
(isBottom && offsetY >= 50)
) {
state.close(toast.key);
}
};
Expand Down Expand Up @@ -127,12 +124,19 @@ const Toast = forwardRef<"div", ToastProps>((props, ref) => {
) : (
<AnimatePresence>
<motion.div
animate={isOut ? {x: "100vw"} : "visible"}
animate={{
opacity: 1,
y: isRegionHovered
? (1 + index - total) * (toastHeight + 5)
: 0 - (total - 1 - index) * 15,
scale: isRegionHovered ? 1 : 1 - (total - 1 - index) * 0.1,
}}
className="fixed bottom-0 right-0 transform -translate-x-1/2"
drag={position.includes("center") ? "y" : "x"}
dragConstraints={{left: 0, right: 0, top: 0, bottom: 0}}
exit="exit"
initial="hidden"
transition={{duration: 0.5}}
exit={{opacity: 0, y: -100}}
initial={{opacity: 0, y: 50, scale: 1}}
transition={{duration: 0.5, ease: "easeOut"}}
variants={toastVariants}
onDragEnd={(_, info) => {
const offsetX = info.offset.x;
Expand Down
22 changes: 20 additions & 2 deletions packages/components/toast/src/use-toast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ export interface ToastProps extends ToastVariantProps {
* Ref to the DOM node.
*/
ref?: ReactRef<HTMLElement | null>;
/**
* Index of the toast
*/
index: number;
/**
* Total number of the toasts
*/
total: number;
/**
* Is the region hovered
*/
isRegionHovered: boolean;
/**
* title of the toast
*/
Expand Down Expand Up @@ -50,11 +62,11 @@ export interface ToastProps extends ToastVariantProps {
*/
classNames?: SlotsToClasses<ToastSlots>;
/**
* Content to be displayed in the end side of the alert
* Content to be displayed in the end side of the toast
*/
endContent?: ReactNode;
/**
* Icon to be displayed in the alert - overrides the default icon
* Icon to be displayed in the toast - overrides the default icon
*/
icon?: ReactNode;
/**
Expand Down Expand Up @@ -154,7 +166,10 @@ export function useToast<T extends ToastProps>(originalProps: UseToastProps<T>)
endContent,
hideIcon = false,
position = "right-bottom",
isRegionHovered,
state,
total,
index,
...otherProps
} = props;

Expand Down Expand Up @@ -258,6 +273,8 @@ export function useToast<T extends ToastProps>(originalProps: UseToastProps<T>)
toast: props.toast,
disableAnimation,
isProgressBarVisible: !!props.toast.timeout,
total,
index,
getToastProps,
getTitleProps,
getContentProps,
Expand All @@ -267,6 +284,7 @@ export function useToast<T extends ToastProps>(originalProps: UseToastProps<T>)
progressBarRef,
endContent,
slots,
isRegionHovered,
};
}

Expand Down

0 comments on commit 73fd38d

Please sign in to comment.