Files
workspace/code/base-project/base-app/src/utils/toast.tsx
T
2025-11-06 17:32:51 +08:00

140 lines
4.1 KiB
TypeScript

import type { IToastProps } from '@gluestack-ui/core/lib/esm/toast/creator/types'
import React, { createContext, useContext, useRef } from 'react'
import { Toast, ToastDescription, ToastTitle, useToast as useGluestackToast } from '@/components/ui/toast'
type ToastVariant = 'solid' | 'outline'
type ToastAction = 'error' | 'warning' | 'success' | 'info' | 'muted'
interface ShowToastOptions extends IToastProps {
title?: React.ReactNode
description?: React.ReactNode
variant?: ToastVariant
action?: ToastAction
}
// 创建全局 toast 实例
let globalToast: any = null
// 创建 Context 用于在组件内部使用
const ToastContext = createContext<ReturnType<typeof useGluestackToast> | null>(null)
export function ToastProviderWrapper({ children }: { children: React.ReactNode }) {
const toast = useGluestackToast()
const toastRef = useRef(toast)
toastRef.current = toast
// 设置全局实例
React.useEffect(() => {
globalToast = {
show: (options: ShowToastOptions) => {
const id = Math.random().toString()
toast.show({
id,
placement: options.placement ?? 'top',
duration: options.duration !== undefined ? options.duration : 3000,
avoidKeyboard: options.avoidKeyboard ?? false,
containerStyle: options.containerStyle,
onCloseComplete: options.onCloseComplete,
render: options.render
? () => options.render!({ id })
: ({ id }) => (
<Toast
nativeID={`toast-${id}`}
variant={options.variant ?? 'solid'}
action={options.action ?? 'muted'}
>
{options.title && <ToastTitle className="font-blod">{options.title}</ToastTitle>}
{options.description && <ToastDescription>{options.description}</ToastDescription>}
</Toast>
),
})
return id
},
close: (id: string) => {
toast.close(id)
},
closeAll: () => {
toast.closeAll()
},
isActive: (id: string) => toast.isActive(id),
}
}, [toast])
return (
<ToastContext.Provider value={toast}>
{children}
</ToastContext.Provider>
)
}
/**
* 自定义 Hook,用于 React 组件内部
*/
export function useToast() {
const toast = useContext(ToastContext)
if (!toast) {
throw new Error('useToast must be used within a ToastProviderWrapper')
}
return {
show: (options: ShowToastOptions) => {
const id = Math.random().toString()
toast.show({
id,
placement: options.placement ?? 'top',
duration: options.duration !== undefined ? options.duration : 3000,
avoidKeyboard: options.avoidKeyboard ?? false,
containerStyle: options.containerStyle,
onCloseComplete: options.onCloseComplete,
render: options.render
? () => options.render!({ id })
: ({ id }) => (
<Toast
nativeID={`toast-${id}`}
variant={options.variant ?? 'solid'}
action={options.action ?? 'muted'}
>
{options.title && <ToastTitle className="font-blod">{options.title}</ToastTitle>}
{options.description && <ToastDescription>{options.description}</ToastDescription>}
</Toast>
),
})
return id
},
close: (id: string) => {
toast.close(id)
},
closeAll: () => {
toast.closeAll()
},
isActive: (id: string) => toast.isActive(id),
}
}
/**
* 全局 toast 实例,可在组件外部使用
*/
export const toast = {
show: (options: ShowToastOptions) => {
if (!globalToast) {
console.warn('Toast not initialized. Make sure ToastProviderWrapper is rendered.')
return ''
}
return globalToast.show(options)
},
close: (id: string) => {
if (globalToast) {
globalToast.close(id)
}
},
closeAll: () => {
if (globalToast) {
globalToast.closeAll()
}
},
isActive: (id: string) => {
return globalToast ? globalToast.isActive(id) : false
},
}