import { forwardRef, useCallback } from "react"; import { createPortal } from "react-dom"; import { Text } from "../text"; import { IconButton } from "../icon-button"; import { Icons } from "../icons"; import { StyledRoot, StyledTitle, StyledDescription, StyledClose, StyledIconContainer, StyledTextTitle, } from "./toast.styled"; import { useToast } from "./toast-provider"; import type { WithTestId } from "../../types"; import type * as Stitches from "@stitches/react"; type RootVariants = Stitches.VariantProps; interface Props { /** * The title of the toast. */ title: string | React.ReactElement<{ children: string }> | null | undefined; /** * The description of the toast. */ description?: string | React.ReactElement<{ children: string }>; /** * The icon displayed on the left. */ icon?: React.ReactElement; /** * Aria label for the close button. */ closeLabel: string; /** * The time in milliseconds that should elapse before automatically closing the toast. */ duration?: number; /** * The style variant of the toast. */ variant?: RootVariants["variant"]; /** * The id of the toast, used to remove it from the dom */ id: number; /** * Props to be passed to the __StyledRoot__ component. */ rootProps?: Omit, "duration">; /** * Props to be passed to the __StyledTitle__ component. */ titleProps?: React.ComponentProps; /** * Props to be passed to the __StyledDescription__ component. */ descriptionProps?: React.ComponentProps; /** * Props to be passed to the __StyledClose__ component. */ closeProps?: Omit, "asChild">; /** * Should content be rendered in a portal? You can pass DOM element to render content in it or true if you want to * render content in the body (this is default because of possible iOS bugs when using Toast from within ScrollArea). * Pass false to disable portal and render content in place. */ portal?: boolean | HTMLElement; } export type ToastProps = WithTestId; /** * Use this to display small piece of information. */ export const Toast = forwardRef( // eslint-disable-next-line max-lines-per-function ( { title, description, closeLabel, duration = 5000, rootProps, titleProps, descriptionProps, closeProps, variant, icon, portal = true, id, ...props }, ref ) => { const { removeToastById } = useToast(); const handleOnOpenChange = useCallback( (open: boolean) => { if (!open) { removeToastById(id); } }, [removeToastById, id] ); const iconVariant = variant === "success" || variant === "warning" ? "ghost-dark" : "ghost"; const content = ( <> {icon && {icon}} {typeof title === "string" ? {title} : title} {description && ( {typeof description === "string" ? ( {description} ) : ( description )} )} ); if (portal) { return createPortal(content, typeof portal === "boolean" ? document.body : portal); } return content; } ); Toast.displayName = "Toast";