62 lines
1.8 KiB
TypeScript
62 lines
1.8 KiB
TypeScript
'use client'
|
|
|
|
import { createContext, useCallback, useContext, useState, type ReactNode } from 'react'
|
|
|
|
type ToastFn = (msg: string, type?: 'success' | 'error') => void
|
|
|
|
const ToastCtx = createContext<ToastFn>(() => {})
|
|
|
|
export function useToast() {
|
|
return useContext(ToastCtx)
|
|
}
|
|
|
|
export function ToastProvider({ children }: { children: ReactNode }) {
|
|
const [state, setState] = useState<{ msg: string; type: 'success' | 'error' } | null>(null)
|
|
|
|
const show = useCallback<ToastFn>((m, type = 'success') => {
|
|
setState({ msg: m, type })
|
|
setTimeout(() => setState(null), type === 'error' ? 4500 : 2800)
|
|
}, [])
|
|
|
|
return (
|
|
<ToastCtx.Provider value={show}>
|
|
{children}
|
|
{state && (
|
|
<div
|
|
style={{
|
|
position: 'fixed',
|
|
left: '50%',
|
|
bottom: 30,
|
|
transform: 'translateX(-50%)',
|
|
zIndex: 90,
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: 10,
|
|
padding: '12px 18px',
|
|
background: 'var(--bg2)',
|
|
border: `1px solid ${state.type === 'error' ? 'var(--errBd, #f87171)' : 'var(--accBd)'}`,
|
|
borderRadius: 12,
|
|
boxShadow: '0 16px 40px rgba(0,0,0,.55)',
|
|
fontSize: 13,
|
|
fontWeight: 500,
|
|
animation: 'scToast .22s ease',
|
|
whiteSpace: 'nowrap',
|
|
}}
|
|
>
|
|
<span
|
|
style={{
|
|
width: 8,
|
|
height: 8,
|
|
borderRadius: '50%',
|
|
background: state.type === 'error' ? '#f87171' : 'var(--acc2)',
|
|
boxShadow: state.type === 'error' ? '0 0 8px #f87171' : '0 0 8px var(--acc2)',
|
|
flexShrink: 0,
|
|
}}
|
|
/>
|
|
{state.msg}
|
|
</div>
|
|
)}
|
|
</ToastCtx.Provider>
|
|
)
|
|
}
|