import type * as React from 'react'; import type { PrimitivePropsWithRef } from '../utils/primitive.cjs'; /** @internal Shared state distributed from {@link ToastProvider} to `ToastViewport` and every `Toast` via context. */ export interface ToastProviderContextValue { label: string; duration: number; /** Swipe direction to dismiss (resolved per toast's writing direction). */ swipeDirection: ToastSwipeDirection; /** Distance in px along `swipeDirection` before drag dismisses. */ swipeThreshold: number; viewportRef: React.RefObject; /** Copies toast text into live region for screen-reader announcement. */ announce: (message: string, priority: 'polite' | 'assertive') => void; } /** Swipe direction to dismiss toast (`'start'` / `'end'` logical, others physical). */ export type ToastSwipeDirection = 'up' | 'down' | 'left' | 'right' | 'start' | 'end'; /** Swipe phase (published as `data-swipe` on root). */ export type ToastSwipePhase = 'start' | 'move' | 'cancel' | 'end'; /** Props for {@link ToastProvider}} (app-level ancestor with duration and announcer). */ export interface ToastProviderProps { /** * The app subtree that renders `ToastViewport` and any mounted `Toast`s. * @default undefined */ children?: React.ReactNode | undefined; /** * Accessible label for the toast region announced by assistive tech. * @default 'Notification' */ label?: string | undefined; /** * Default auto-dismiss duration (ms), used by toasts without their own setting. * @default 5000 */ duration?: number | undefined; /** * Swipe direction to dismiss (`'start'` / `'end'` mirror under RTL; gesture is addition to close button / Escape / timer, not replacement per WCAG 2.2.1). * @default 'end' */ swipeDirection?: ToastSwipeDirection | undefined; /** * Distance in px along `swipeDirection` before drag dismisses. * @default 50 */ swipeThreshold?: number | undefined; } /** Props for {@link ToastViewport}, the fixed region that hosts and orders rendered toasts. */ export interface ToastViewportProps extends PrimitivePropsWithRef<'div'> { /** * Keys that move focus into the region from anywhere on the page, and (with `Shift`) back out again; every entry but the last is a modifier. * @default ['F6'] */ hotkey?: readonly string[] | undefined; } /** @internal Shared state distributed from a single {@link Toast} to its parts via context. */ export interface ToastContextValue { onClose: () => void; /** Id `ToastTitle` renders with; root's `aria-labelledby` once mounted. */ titleId: string; /** Id `ToastDescription` renders with; root's `aria-describedby` once mounted. */ descriptionId: string; /** Called by `ToastTitle` on mount/unmount (prevents dangling `aria-labelledby`). */ setHasTitle: (present: boolean) => void; /** Called by `ToastDescription` on mount/unmount (prevents dangling `aria-describedby`). */ setHasDescription: (present: boolean) => void; } /** Props for a single {@link Toast} notification. Mount one per notification. */ export interface ToastProps extends PrimitivePropsWithRef<'div'> { /** * Whether the toast is open, for controlled usage. Pair with `onOpenChange`. * @default undefined — uncontrolled, tracks its own open state internally. */ open?: boolean | undefined; /** * Whether the toast is initially open, for uncontrolled usage. * @default true */ defaultOpen?: boolean | undefined; /** * Fires with new open state (drop toast from state when becomes false). * @default undefined — no-op. */ onOpenChange?: ((open: boolean) => void) | undefined; /** * Fires once open-state change finishes (after animation); where `forceMount`ed toast drops from state. * @default undefined — no-op. */ onOpenChangeComplete?: ((open: boolean) => void) | undefined; /** * Keep toast in DOM while closed (for exit animation `data-state="closed"`). * @default false */ forceMount?: boolean | undefined; /** * Auto-dismiss duration (ms); `Infinity` disables; pauses on hover / focus. * @default undefined — falls back to `ToastProvider`'s `duration`. */ duration?: number | undefined; /** * Which live region text copies to (`'assertive'` for errors/warnings user must act on, `'polite'` otherwise). * @default 'background' */ type?: 'foreground' | 'background' | undefined; /** * Fires on Escape (call `preventDefault()` to keep open). * @default undefined — no-op. */ onEscapeKeyDown?: ((event: KeyboardEvent) => void) | undefined; /** * Fires when timer pauses (held by hover / focus / hidden page; edge-triggered). * @default undefined — no-op. */ onPause?: (() => void) | undefined; /** * Callback fired when auto-dismiss timer restarts on remaining time. * @default undefined — no-op. */ onResume?: (() => void) | undefined; } /** Props for {@link ToastTitle}, the toast's heading. */ export interface ToastTitleProps extends PrimitivePropsWithRef<'div'> { } /** Props for {@link ToastDescription}, the toast's body text. */ export interface ToastDescriptionProps extends PrimitivePropsWithRef<'div'> { } /** Props for {@link ToastAction}, an actionable button rendered inside a toast (e.g. "Undo"). */ export interface ToastActionProps extends PrimitivePropsWithRef<'button'> { /** Describes the action for assistive tech users who can't easily reach it. */ altText: string; } /** Props for {@link ToastClose}, a button that dismisses the toast. */ export interface ToastCloseProps extends PrimitivePropsWithRef<'button'> { } /** * Where the toast region sits on screen. Emitted as `data-placement` on the * viewport — headless, so the design layer does the actual positioning. */ export type ToastPlacement = 'top-start' | 'top' | 'top-end' | 'bottom-start' | 'bottom' | 'bottom-end'; /** Semantic kind of a queued toast. Drives `data-type` and which of the announcer's two live regions reads it. */ export type ToasterToastType = 'info' | 'success' | 'warning' | 'error' | 'loading'; /** An actionable button rendered inside a queued toast (the "Undo" affordance). */ export interface ToasterToastAction { /** Visible button text. */ label: string; /** Describes the action for assistive tech users who can't easily reach it, per `ToastAction`. */ altText: string; /** Runs on click. The toast closes either way. */ onClick?: (() => void) | undefined; } /** What {@link ToasterStore.create} and {@link ToasterStore.update} accept. */ export interface ToasterToastOptions { /** * Stable id, so a later `update`/`dismiss` can address this toast; reusing a queued id replaces that entry. * @default undefined — generated. */ id?: string | undefined; /** Heading text, rendered as `ToastTitle`. @default undefined — no title. */ title?: string | undefined; /** Body text, rendered as `ToastDescription`. @default undefined — no description. */ description?: string | undefined; /** Semantic kind. @default 'info' */ type?: ToasterToastType | undefined; /** * Auto-dismiss duration (ms); `Infinity` never auto-dismisses. * @default undefined — the toaster's `duration`, except `type: 'loading'`, which is `Infinity`. */ duration?: number | undefined; /** Optional action button. @default undefined — no action button. */ action?: ToasterToastAction | undefined; /** Accessible name for the close button. @default 'Dismiss' */ closeLabel?: string | undefined; } /** One queued toast, as handed to the renderer. */ export interface ToasterToast extends ToasterToastOptions { id: string; type: ToasterToastType; /** Bumped on every `update`; renderers key on `id:revision`, so an updated toast remounts and re-announces. */ revision: number; } /** Options for {@link createToaster}. */ export interface ToasterOptions { /** * How many toasts are visible at once; the rest wait in FIFO order. * @default 3 */ max?: number | undefined; /** Where the region sits, as `data-placement` on the viewport. @default 'bottom-end' */ placement?: ToastPlacement | undefined; /** Default auto-dismiss duration (ms) for queued toasts. @default 5000 */ duration?: number | undefined; /** Accessible label for the toast region landmark. @default 'Notifications' */ label?: string | undefined; } /** Messages a {@link ToasterStore.promise} call swaps between as the promise settles. */ export interface ToasterPromiseMessages { /** Shown immediately, as `type: 'loading'` (no auto-dismiss). */ loading: ToasterToastOptions; /** Shown on fulfilment, as `type: 'success'`. */ success: ToasterToastOptions | ((value: T) => ToasterToastOptions); /** Shown on rejection, as `type: 'error'`. The rejection is re-thrown either way. */ error: ToasterToastOptions | ((error: unknown) => ToasterToastOptions); } /** * An imperative toast queue callable from anywhere with no React tree or hook, rendered by a mounted {@link Toaster}. */ export interface ToasterStore { /** See {@link ToasterOptions.max}. */ readonly max: number; /** See {@link ToasterOptions.placement}. */ readonly placement: ToastPlacement; /** See {@link ToasterOptions.duration}. */ readonly duration: number; /** See {@link ToasterOptions.label}. */ readonly label: string; /** Subscribes to queue changes. Returns its own unsubscribe. */ subscribe: (listener: () => void) => () => void; /** The currently visible toasts (at most `max`) — a stable reference until the queue changes. */ getToasts: () => readonly ToasterToast[]; /** Queues a toast and returns its id. */ create(options?: ToasterToastOptions): string; /** Merges `options` into a queued toast, bumping its revision. No-ops for an unknown id. */ update(id: string, options: ToasterToastOptions): void; /** Removes one toast, or the whole queue when called with no id. */ dismiss(id?: string): void; /** Shows a loading toast, then swaps it for success/error when `promise` settles. Re-throws rejections. */ promise(promise: Promise, messages: ToasterPromiseMessages, options?: ToasterToastOptions): Promise; } /** Props for {@link Toaster}. Everything else lands on the underlying `ToastViewport`. */ export interface ToasterProps extends ToastViewportProps { /** The queue to render, from {@link createToaster}. */ toaster: ToasterStore; }