"use client"; import { Toast } from "@base-ui/react/toast"; import { CircleAlertIcon, CircleCheckIcon, InfoIcon, LoaderCircleIcon, TriangleAlertIcon, XIcon, } from "lucide-react"; import type React from "react"; import { cn } from "@/lib/utils"; import { buttonVariants } from "@/components/ui/button"; const TOAST_ICONS = { error: CircleAlertIcon, info: InfoIcon, loading: LoaderCircleIcon, success: CircleCheckIcon, warning: TriangleAlertIcon, } as const; type SwipeDirection = "up" | "down" | "left" | "right"; type ToastData = { rootProps?: Omit< React.ComponentProps, "children" | "className" | "swipeDirection" | "toast" >; tooltipStyle?: boolean; }; function getSwipeDirection(position: ToastPosition): SwipeDirection[] { const verticalDirection: SwipeDirection = position.startsWith("top") ? "up" : "down"; if (position.includes("center")) { return [verticalDirection]; } if (position.includes("left")) { return ["left", verticalDirection]; } return ["right", verticalDirection]; } function upsertReplayClassName(toast: { type?: string; updateKey?: number; }): string | undefined { const k = toast.updateKey ?? 0; if (k <= 0) return undefined; const isEven = k % 2 === 0; if (toast.type === "error") { return isEven ? "animate-toast-error-even" : "animate-toast-error-odd"; } return isEven ? "animate-toast-success-even" : "animate-toast-success-odd"; } function Toasts({ position, portalProps, }: { position: ToastPosition; portalProps?: React.ComponentProps; }): React.ReactElement { const { toasts } = Toast.useToastManager(); const swipeDirection = getSwipeDirection(position); return ( {toasts.map((toast) => { const Icon = toast.type ? TOAST_ICONS[toast.type as keyof typeof TOAST_ICONS] : null; const toastData = toast.data as ToastData | undefined; return (
{Icon && (
)}
{toast.actionProps && ( {toast.actionProps.children} )}
); })}
); } function AnchoredToasts({ portalProps, }: { portalProps?: React.ComponentProps; }): React.ReactElement { const { toasts } = Toast.useToastManager(); return ( {toasts.map((toast) => { const Icon = toast.type ? TOAST_ICONS[toast.type as keyof typeof TOAST_ICONS] : null; const toastData = toast.data as ToastData | undefined; const tooltipStyle = toastData?.tooltipStyle ?? false; const positionerProps = toast.positionerProps; if (!positionerProps?.anchor) { return null; } return ( {tooltipStyle ? ( ) : (
{Icon && (
)}
{toast.actionProps && ( {toast.actionProps.children} )}
)}
); })}
); } export const toastManager: ReturnType = Toast.createToastManager(); export const anchoredToastManager: ReturnType = Toast.createToastManager(); export type ToastPosition = | "top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right"; export interface ToastProviderProps extends Toast.Provider.Props { position?: ToastPosition; portalProps?: React.ComponentProps; } export function ToastProvider({ children, position = "bottom-right", portalProps, ...props }: ToastProviderProps): React.ReactElement { return ( {children} ); } export interface AnchoredToastProviderProps extends Toast.Provider.Props { portalProps?: React.ComponentProps; } export function AnchoredToastProvider({ children, portalProps, ...props }: AnchoredToastProviderProps): React.ReactElement { return ( {children} ); } export { Toast as ToastPrimitive };