From 6823ce5b94456f3f53a1f2943b2b6a48a7964107 Mon Sep 17 00:00:00 2001 From: Maharshi Alpesh Date: Wed, 1 Jan 2025 00:30:38 +0530 Subject: [PATCH] chore: improving the animations --- .../components/toast/src/toast-provider.tsx | 11 ++--- .../components/toast/src/toast-region.tsx | 13 ++--- packages/components/toast/src/toast.tsx | 39 +++++---------- packages/components/toast/src/use-toast.ts | 48 ++++++++++++++++++- 4 files changed, 66 insertions(+), 45 deletions(-) diff --git a/packages/components/toast/src/toast-provider.tsx b/packages/components/toast/src/toast-provider.tsx index 1600736992..2da2db8748 100644 --- a/packages/components/toast/src/toast-provider.tsx +++ b/packages/components/toast/src/toast-provider.tsx @@ -16,21 +16,18 @@ interface ToastProviderProps { | "center-top"; } -export const getToastQueue = (maxVisibleToasts: number) => { +export const getToastQueue = () => { if (!globalToastQueue) { globalToastQueue = new ToastQueue({ - maxVisibleToasts, + maxVisibleToasts: Infinity, }); } return globalToastQueue; }; -export const ToastProvider = ({ - maxVisibleToasts = 3, - position = "right-bottom", -}: ToastProviderProps) => { - const toastQueue = useToastQueue(getToastQueue(maxVisibleToasts)); +export const ToastProvider = ({position = "right-bottom"}: ToastProviderProps) => { + const toastQueue = useToastQueue(getToastQueue()); if (toastQueue.visibleToasts.length == 0) { return null; diff --git a/packages/components/toast/src/toast-region.tsx b/packages/components/toast/src/toast-region.tsx index eb124adb13..0629433a40 100644 --- a/packages/components/toast/src/toast-region.tsx +++ b/packages/components/toast/src/toast-region.tsx @@ -1,4 +1,4 @@ -import {useRef} from "react"; +import {useRef, useState} from "react"; import {useToastRegion, AriaToastRegionProps} from "@react-aria/toast"; import {QueuedToast, ToastState} from "@react-stately/toast"; import {createPortal} from "react-dom"; @@ -40,14 +40,7 @@ export function ToastRegion({ "center-top": "top-0 left-1/2 -translate-x-1/2", }; const positionStyle = position ? positionStyles[position] : positionStyles["right-bottom"]; - - const toasts = document.querySelectorAll("[data-toast]"); - let height = 0; - - for (let i = toasts.length - 1; i >= 0; i--) { - toasts[i].style.setProperty(`--toast-gap`, `${height}px`); - height = height + toasts[i].offsetHeight; - } + const [heights, setHeights] = useState([]); return createPortal(
({ state={toastQueue} toast={toast} {...toast.content} + heights={heights} index={index} isRegionHovered={isHovered} position={position} + setHeights={setHeights} total={toastQueue.visibleToasts.length} /> ); diff --git a/packages/components/toast/src/toast.tsx b/packages/components/toast/src/toast.tsx index cc1fbc9adf..0d38fa8c47 100644 --- a/packages/components/toast/src/toast.tsx +++ b/packages/components/toast/src/toast.tsx @@ -48,6 +48,9 @@ const Toast = forwardRef<"div", ToastProps>((props, ref) => { getDescriptionProps, getCloseButtonProps, getIconProps, + liftHeight, + initialHeight, + frontHeight, } = useToast({ ...props, ref, @@ -109,31 +112,14 @@ const Toast = forwardRef<"div", ToastProps>((props, ref) => { ); const positionStyles: Record = { - "right-bottom": "bottom-0 right-0 max-auto w-max", + "right-bottom": "bottom-0 right-0 mx-auto w-max", "left-bottom": "bottom-0 left-0 mx-auto w-max", "center-bottom": "bottom-0 left-0 right-0 mx-auto w-max", - "right-top": "top-0 right-0 max-auto w-max", + "right-top": "top-0 right-0 mx-auto w-max", "left-top": "top-0 left-0 mx-auto w-max", - "center-top": "top-0 left-0 right-0 mx-auto w-max", }; const positionStyle = position ? positionStyles[position] : positionStyles["right-bottom"]; const multiplier = position.includes("top") ? -1 : 1; - let gap = 0; - let currentHeight = 0; - - if (domRef.current) { - const styles = getComputedStyle(domRef.current); - - gap = parseFloat(styles.getPropertyValue("--toast-gap")) || 0; - currentHeight = domRef.current.offsetHeight || 0; - } - - const toasts = document.querySelectorAll("[data-toast]"); - let height = 0; - - if (toasts.length > 0) { - height = toasts[toasts.length - 1].offsetHeight; - } return ( <> @@ -143,20 +129,17 @@ const Toast = forwardRef<"div", ToastProps>((props, ref) => { { const offsetX = info.offset.x; diff --git a/packages/components/toast/src/use-toast.ts b/packages/components/toast/src/use-toast.ts index bb8ad2e303..036485d270 100644 --- a/packages/components/toast/src/use-toast.ts +++ b/packages/components/toast/src/use-toast.ts @@ -9,7 +9,7 @@ import { import {toast as toastTheme} from "@nextui-org/theme"; import {ReactRef, useDOMRef} from "@nextui-org/react-utils"; import {clsx, dataAttr, isEmpty, objectToDeps} from "@nextui-org/shared-utils"; -import {ReactNode, useCallback, useEffect, useMemo, useRef, useState} from "react"; +import {ReactNode, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState} from "react"; import {useToast as useToastAria, AriaToastProps} from "@react-aria/toast"; import {mergeProps} from "@react-aria/utils"; import {QueuedToast, ToastState} from "@react-stately/toast"; @@ -93,6 +93,8 @@ export interface ToastProps extends ToastVariantProps { interface Props extends HTMLNextUIProps<"div">, ToastProps { toast: QueuedToast; state: ToastState; + heights: number[]; + setHeights: (val: number[]) => void; } export type UseToastProps = Props & @@ -170,6 +172,8 @@ export function useToast(originalProps: UseToastProps) state, total = 1, index = 0, + heights, + setHeights, ...otherProps } = props; @@ -184,6 +188,45 @@ export function useToast(originalProps: UseToastProps) domRef, ); + const [mounted, setMounted] = useState(false); + + useEffect(() => { + setMounted(true); + }, []); + + const [initialHeight, setInitialHeight] = useState(0); + + useLayoutEffect(() => { + if (!domRef.current || !mounted) { + return; + } + const toastNode = domRef.current; + const originalHeight = toastNode.style.height; + + toastNode.style.height = "auto"; + const newHeight = toastNode.getBoundingClientRect().height; + + toastNode.style.height = originalHeight; + + setInitialHeight((prevHeight) => (prevHeight !== newHeight ? newHeight : prevHeight)); + const updatedHeights = [...heights]; + + if (updatedHeights.length > index) { + updatedHeights[index] = newHeight; + } else { + updatedHeights.push(newHeight); + } + setHeights(updatedHeights); + }, [mounted, total, setHeights, index]); + + let liftHeight = 4; + + for (let idx = index + 1; idx < total; idx++) { + liftHeight += 4 + heights[idx]; + } + + const frontHeight = heights[heights.length - 1]; + const slots = useMemo( () => toastTheme({ @@ -286,6 +329,9 @@ export function useToast(originalProps: UseToastProps) endContent, slots, isRegionHovered, + liftHeight, + frontHeight, + initialHeight, }; }