140 lines
4.1 KiB
TypeScript
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
|
|
},
|
|
}
|