// The one way to render a toast queue: the store's VISIBLE slice, inside the region that owns the // live semantics. The half that was missing — `Toast` and `ToastRegion` shipped with no store, so // every app wrote its own queue, its own dwell and its own pause rules. // // Toasts over the cap are not drawn and not counted on screen: they are still queued, and their // dwell has not started. A "+3 more" badge would be a message about messages, in a corner the // reader is already being asked to look away to. import type { JSX } from 'solid-js'; import type { Politeness } from '../a11y'; import { type ToastItem, visibleToasts } from '../toast/toast-state'; import type { ToastStore } from '../toast/toast-store'; import { useToasts } from '../toast/use-toasts'; import { Button } from './Button'; import { Toast, type ToastPlacement, ToastRegion } from './Toast'; export interface ToasterProps { /** The queue. One per app — `toastStore()` in the island that mounts this. */ store: ToastStore; /** Already-translated landmark name, e.g. "Notifications". */ label: string; politeness?: Politeness | undefined; placement?: ToastPlacement | undefined; /** Already-translated; falls back to the `ui.dismiss` catalog key. */ dismissLabel?: string | undefined; class?: string | undefined; } export function Toaster(props: ToasterProps): JSX.Element { const queue = useToasts(props.store); /** * One undo-shaped control, or nothing. A toast never carries the only affordance for an action: * it disappears on a timer, and a keyboard user who was typing when it arrived never reached it. * Taking the undo also dismisses — the offer has been answered. */ const actionSlot = (item: ToastItem): JSX.Element => { const action = item.action; if (action === undefined) return null; return ( ); }; return ( props.store.hold(reason)} onRelease={(reason) => props.store.release(reason)} > {visibleToasts(queue()).map((item) => ( props.store.dismiss(item.id)} > {item.message} ))} ); }