|
|
|
@ -1,4 +1,4 @@
|
|
|
|
import { createContext, useCallback, useContext, useMemo, useRef, useState, type ReactNode } from 'react'
|
|
|
|
import { createContext, useCallback, useContext, useMemo, useState, type ReactNode } from 'react'
|
|
|
|
import { dismissToast, initialToasts, pushToast, type Toast, type ToastState } from './toast-store'
|
|
|
|
import { dismissToast, initialToasts, pushToast, type Toast, type ToastState } from './toast-store'
|
|
|
|
|
|
|
|
|
|
|
|
const AUTO_DISMISS_MS = 4000
|
|
|
|
const AUTO_DISMISS_MS = 4000
|
|
|
|
@ -9,13 +9,14 @@ const Ctx = createContext<ToastApi | null>(null)
|
|
|
|
/** Mount once at app root; renders the bottom-right stack. */
|
|
|
|
/** Mount once at app root; renders the bottom-right stack. */
|
|
|
|
export function ToastProvider(props: { children: ReactNode }) {
|
|
|
|
export function ToastProvider(props: { children: ReactNode }) {
|
|
|
|
const [state, setState] = useState<ToastState>(initialToasts)
|
|
|
|
const [state, setState] = useState<ToastState>(initialToasts)
|
|
|
|
const stateRef = useRef(state)
|
|
|
|
|
|
|
|
stateRef.current = state
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const push = useCallback((tone: Toast['tone'], message: string) => {
|
|
|
|
const push = useCallback((tone: Toast['tone'], message: string) => {
|
|
|
|
const id = stateRef.current.nextId
|
|
|
|
setState((s) => {
|
|
|
|
setState((s) => pushToast(s, tone, message))
|
|
|
|
const next = pushToast(s, tone, message)
|
|
|
|
window.setTimeout(() => setState((s) => dismissToast(s, id)), AUTO_DISMISS_MS)
|
|
|
|
const id = next.toasts[next.toasts.length - 1]!.id
|
|
|
|
|
|
|
|
window.setTimeout(() => setState((s2) => dismissToast(s2, id)), AUTO_DISMISS_MS)
|
|
|
|
|
|
|
|
return next
|
|
|
|
|
|
|
|
})
|
|
|
|
}, [])
|
|
|
|
}, [])
|
|
|
|
|
|
|
|
|
|
|
|
const api = useMemo<ToastApi>(() => ({
|
|
|
|
const api = useMemo<ToastApi>(() => ({
|
|
|
|
|