import React, { useCallback, useMemo, useState } from 'react'; import * as BaseToast from '@radix-ui/react-toast'; import { createContextSelector } from '../utils/react'; import { type ToastStatus, Toast } from './toast'; import { ToastViewport } from './toast-viewport'; export type ToastContextValues = { vertical: 'top' | 'bottom'; horizontal: 'left' | 'right'; enqueueToast: (text: string, options?: EnqueuedToastOptions) => ToastId; closeToast: (id: ToastId) => void; closeAll: () => void; }; type ToastId = number; const getToastId = (() => { let nextToastId = 1; return () => nextToastId++; })(); type EnqueuedToastOptions = { status?: ToastStatus; duration?: number; closable?: boolean; }; type EnqueuedToast = { id: ToastId; text: string; options?: EnqueuedToastOptions; }; export interface ToastProviderProps extends Omit { vertical?: 'top' | 'bottom'; horizontal?: 'left' | 'right'; maxQueueSize?: number; } const [ToastContext, useToastContext] = createContextSelector({ vertical: 'bottom', horizontal: 'right', enqueueToast: () => 0, closeToast: () => {}, closeAll: () => {}, }); export const ToastProvider = ({ children, vertical = 'bottom', horizontal = 'right', maxQueueSize = 3, ...rest }: ToastProviderProps) => { const [queue, setQueue] = useState([]); const addToQueue = useCallback( (text: string, options?: EnqueuedToastOptions) => { const id = getToastId(); setQueue(state => [{ id, text, options }, ...state].slice(0, maxQueueSize), ); return id; }, [maxQueueSize], ); const removeFromQueue = useCallback( (id: ToastId) => setQueue(state => state.filter(entry => entry.id !== id)), [], ); const clearQueue = useCallback(() => setQueue([]), []); const contextValue = useMemo( () => ({ vertical, horizontal, enqueueToast: addToQueue, closeToast: removeFromQueue, closeAll: clearQueue, }), [vertical, horizontal, addToQueue, removeFromQueue, clearQueue], ); const renderQueueItem = useCallback( (toast: EnqueuedToast) => ( {toast.text} ), [vertical, horizontal], ); return ( {children} {queue.map(item => renderQueueItem(item))} ); }; export { useToastContext };