@tamagui/v2-toast 2.0.0-1769464493958
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/Toast.cjs +170 -0
- package/dist/cjs/Toast.js +119 -0
- package/dist/cjs/Toast.js.map +6 -0
- package/dist/cjs/Toast.native.js +174 -0
- package/dist/cjs/Toast.native.js.map +1 -0
- package/dist/cjs/ToastAnnounce.cjs +97 -0
- package/dist/cjs/ToastAnnounce.js +72 -0
- package/dist/cjs/ToastAnnounce.js.map +6 -0
- package/dist/cjs/ToastAnnounce.native.js +105 -0
- package/dist/cjs/ToastAnnounce.native.js.map +1 -0
- package/dist/cjs/ToastImperative.cjs +100 -0
- package/dist/cjs/ToastImperative.js +71 -0
- package/dist/cjs/ToastImperative.js.map +6 -0
- package/dist/cjs/ToastImperative.native.js +122 -0
- package/dist/cjs/ToastImperative.native.js.map +1 -0
- package/dist/cjs/ToastImpl.cjs +292 -0
- package/dist/cjs/ToastImpl.js +227 -0
- package/dist/cjs/ToastImpl.js.map +6 -0
- package/dist/cjs/ToastImpl.native.js +327 -0
- package/dist/cjs/ToastImpl.native.js.map +1 -0
- package/dist/cjs/ToastItem.cjs +466 -0
- package/dist/cjs/ToastItem.js +356 -0
- package/dist/cjs/ToastItem.js.map +6 -0
- package/dist/cjs/ToastItem.native.js +547 -0
- package/dist/cjs/ToastItem.native.js.map +1 -0
- package/dist/cjs/ToastPortal.cjs +44 -0
- package/dist/cjs/ToastPortal.js +26 -0
- package/dist/cjs/ToastPortal.js.map +6 -0
- package/dist/cjs/ToastPortal.native.js +47 -0
- package/dist/cjs/ToastPortal.native.js.map +1 -0
- package/dist/cjs/ToastProvider.cjs +146 -0
- package/dist/cjs/ToastProvider.js +105 -0
- package/dist/cjs/ToastProvider.js.map +6 -0
- package/dist/cjs/ToastProvider.native.js +159 -0
- package/dist/cjs/ToastProvider.native.js.map +1 -0
- package/dist/cjs/ToastState.cjs +248 -0
- package/dist/cjs/ToastState.js +160 -0
- package/dist/cjs/ToastState.js.map +6 -0
- package/dist/cjs/ToastState.native.js +257 -0
- package/dist/cjs/ToastState.native.js.map +1 -0
- package/dist/cjs/ToastViewport.cjs +278 -0
- package/dist/cjs/ToastViewport.js +263 -0
- package/dist/cjs/ToastViewport.js.map +6 -0
- package/dist/cjs/ToastViewport.native.js +316 -0
- package/dist/cjs/ToastViewport.native.js.map +1 -0
- package/dist/cjs/Toaster.cjs +219 -0
- package/dist/cjs/Toaster.js +177 -0
- package/dist/cjs/Toaster.js.map +6 -0
- package/dist/cjs/Toaster.native.js +279 -0
- package/dist/cjs/Toaster.native.js.map +1 -0
- package/dist/cjs/constants.cjs +28 -0
- package/dist/cjs/constants.js +22 -0
- package/dist/cjs/constants.js.map +6 -0
- package/dist/cjs/constants.native.js +31 -0
- package/dist/cjs/constants.native.js.map +1 -0
- package/dist/cjs/createNativeToast.cjs +51 -0
- package/dist/cjs/createNativeToast.js +44 -0
- package/dist/cjs/createNativeToast.js.map +6 -0
- package/dist/cjs/createNativeToast.native.js +47 -0
- package/dist/cjs/createNativeToast.native.js.map +1 -0
- package/dist/cjs/index.cjs +28 -0
- package/dist/cjs/index.js +22 -0
- package/dist/cjs/index.js.map +6 -0
- package/dist/cjs/index.native.js +31 -0
- package/dist/cjs/index.native.js.map +1 -0
- package/dist/cjs/types.cjs +16 -0
- package/dist/cjs/types.js +14 -0
- package/dist/cjs/types.js.map +6 -0
- package/dist/cjs/types.native.js +19 -0
- package/dist/cjs/types.native.js.map +1 -0
- package/dist/cjs/useDragGesture.cjs +129 -0
- package/dist/cjs/useDragGesture.js +100 -0
- package/dist/cjs/useDragGesture.js.map +6 -0
- package/dist/cjs/useDragGesture.native.js +146 -0
- package/dist/cjs/useDragGesture.native.js.map +1 -0
- package/dist/esm/Toast.js +107 -0
- package/dist/esm/Toast.js.map +6 -0
- package/dist/esm/Toast.mjs +131 -0
- package/dist/esm/Toast.mjs.map +1 -0
- package/dist/esm/Toast.native.js +132 -0
- package/dist/esm/Toast.native.js.map +1 -0
- package/dist/esm/ToastAnnounce.js +55 -0
- package/dist/esm/ToastAnnounce.js.map +6 -0
- package/dist/esm/ToastAnnounce.mjs +62 -0
- package/dist/esm/ToastAnnounce.mjs.map +1 -0
- package/dist/esm/ToastAnnounce.native.js +67 -0
- package/dist/esm/ToastAnnounce.native.js.map +1 -0
- package/dist/esm/ToastImperative.js +50 -0
- package/dist/esm/ToastImperative.js.map +6 -0
- package/dist/esm/ToastImperative.mjs +63 -0
- package/dist/esm/ToastImperative.mjs.map +1 -0
- package/dist/esm/ToastImperative.native.js +82 -0
- package/dist/esm/ToastImperative.native.js.map +1 -0
- package/dist/esm/ToastImpl.js +225 -0
- package/dist/esm/ToastImpl.js.map +6 -0
- package/dist/esm/ToastImpl.mjs +256 -0
- package/dist/esm/ToastImpl.mjs.map +1 -0
- package/dist/esm/ToastImpl.native.js +288 -0
- package/dist/esm/ToastImpl.native.js.map +1 -0
- package/dist/esm/ToastItem.js +339 -0
- package/dist/esm/ToastItem.js.map +6 -0
- package/dist/esm/ToastItem.mjs +432 -0
- package/dist/esm/ToastItem.mjs.map +1 -0
- package/dist/esm/ToastItem.native.js +510 -0
- package/dist/esm/ToastItem.native.js.map +1 -0
- package/dist/esm/ToastPortal.js +13 -0
- package/dist/esm/ToastPortal.js.map +6 -0
- package/dist/esm/ToastPortal.mjs +21 -0
- package/dist/esm/ToastPortal.mjs.map +1 -0
- package/dist/esm/ToastPortal.native.js +21 -0
- package/dist/esm/ToastPortal.native.js.map +1 -0
- package/dist/esm/ToastProvider.js +87 -0
- package/dist/esm/ToastProvider.js.map +6 -0
- package/dist/esm/ToastProvider.mjs +108 -0
- package/dist/esm/ToastProvider.mjs.map +1 -0
- package/dist/esm/ToastProvider.native.js +118 -0
- package/dist/esm/ToastProvider.native.js.map +1 -0
- package/dist/esm/ToastState.js +144 -0
- package/dist/esm/ToastState.js.map +6 -0
- package/dist/esm/ToastState.mjs +224 -0
- package/dist/esm/ToastState.mjs.map +1 -0
- package/dist/esm/ToastState.native.js +230 -0
- package/dist/esm/ToastState.native.js.map +1 -0
- package/dist/esm/ToastViewport.js +250 -0
- package/dist/esm/ToastViewport.js.map +6 -0
- package/dist/esm/ToastViewport.mjs +241 -0
- package/dist/esm/ToastViewport.mjs.map +1 -0
- package/dist/esm/ToastViewport.native.js +276 -0
- package/dist/esm/ToastViewport.native.js.map +1 -0
- package/dist/esm/Toaster.js +160 -0
- package/dist/esm/Toaster.js.map +6 -0
- package/dist/esm/Toaster.mjs +185 -0
- package/dist/esm/Toaster.mjs.map +1 -0
- package/dist/esm/Toaster.native.js +242 -0
- package/dist/esm/Toaster.native.js.map +1 -0
- package/dist/esm/constants.js +6 -0
- package/dist/esm/constants.js.map +6 -0
- package/dist/esm/constants.mjs +4 -0
- package/dist/esm/constants.mjs.map +1 -0
- package/dist/esm/constants.native.js +4 -0
- package/dist/esm/constants.native.js.map +1 -0
- package/dist/esm/createNativeToast.js +28 -0
- package/dist/esm/createNativeToast.js.map +6 -0
- package/dist/esm/createNativeToast.mjs +27 -0
- package/dist/esm/createNativeToast.mjs.map +1 -0
- package/dist/esm/createNativeToast.native.js +20 -0
- package/dist/esm/createNativeToast.native.js.map +1 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +6 -0
- package/dist/esm/index.mjs +4 -0
- package/dist/esm/index.mjs.map +1 -0
- package/dist/esm/index.native.js +4 -0
- package/dist/esm/index.native.js.map +1 -0
- package/dist/esm/types.js +1 -0
- package/dist/esm/types.js.map +6 -0
- package/dist/esm/types.mjs +2 -0
- package/dist/esm/types.mjs.map +1 -0
- package/dist/esm/types.native.js +2 -0
- package/dist/esm/types.native.js.map +1 -0
- package/dist/esm/useDragGesture.js +76 -0
- package/dist/esm/useDragGesture.js.map +6 -0
- package/dist/esm/useDragGesture.mjs +95 -0
- package/dist/esm/useDragGesture.mjs.map +1 -0
- package/dist/esm/useDragGesture.native.js +109 -0
- package/dist/esm/useDragGesture.native.js.map +1 -0
- package/dist/jsx/Toast.js +107 -0
- package/dist/jsx/Toast.js.map +6 -0
- package/dist/jsx/Toast.mjs +131 -0
- package/dist/jsx/Toast.mjs.map +1 -0
- package/dist/jsx/Toast.native.js +174 -0
- package/dist/jsx/Toast.native.js.map +1 -0
- package/dist/jsx/ToastAnnounce.js +55 -0
- package/dist/jsx/ToastAnnounce.js.map +6 -0
- package/dist/jsx/ToastAnnounce.mjs +62 -0
- package/dist/jsx/ToastAnnounce.mjs.map +1 -0
- package/dist/jsx/ToastAnnounce.native.js +105 -0
- package/dist/jsx/ToastAnnounce.native.js.map +1 -0
- package/dist/jsx/ToastImperative.js +50 -0
- package/dist/jsx/ToastImperative.js.map +6 -0
- package/dist/jsx/ToastImperative.mjs +63 -0
- package/dist/jsx/ToastImperative.mjs.map +1 -0
- package/dist/jsx/ToastImperative.native.js +122 -0
- package/dist/jsx/ToastImperative.native.js.map +1 -0
- package/dist/jsx/ToastImpl.js +225 -0
- package/dist/jsx/ToastImpl.js.map +6 -0
- package/dist/jsx/ToastImpl.mjs +256 -0
- package/dist/jsx/ToastImpl.mjs.map +1 -0
- package/dist/jsx/ToastImpl.native.js +327 -0
- package/dist/jsx/ToastImpl.native.js.map +1 -0
- package/dist/jsx/ToastItem.js +339 -0
- package/dist/jsx/ToastItem.js.map +6 -0
- package/dist/jsx/ToastItem.mjs +432 -0
- package/dist/jsx/ToastItem.mjs.map +1 -0
- package/dist/jsx/ToastItem.native.js +547 -0
- package/dist/jsx/ToastItem.native.js.map +1 -0
- package/dist/jsx/ToastPortal.js +13 -0
- package/dist/jsx/ToastPortal.js.map +6 -0
- package/dist/jsx/ToastPortal.mjs +21 -0
- package/dist/jsx/ToastPortal.mjs.map +1 -0
- package/dist/jsx/ToastPortal.native.js +47 -0
- package/dist/jsx/ToastPortal.native.js.map +1 -0
- package/dist/jsx/ToastProvider.js +87 -0
- package/dist/jsx/ToastProvider.js.map +6 -0
- package/dist/jsx/ToastProvider.mjs +108 -0
- package/dist/jsx/ToastProvider.mjs.map +1 -0
- package/dist/jsx/ToastProvider.native.js +159 -0
- package/dist/jsx/ToastProvider.native.js.map +1 -0
- package/dist/jsx/ToastState.js +144 -0
- package/dist/jsx/ToastState.js.map +6 -0
- package/dist/jsx/ToastState.mjs +224 -0
- package/dist/jsx/ToastState.mjs.map +1 -0
- package/dist/jsx/ToastState.native.js +257 -0
- package/dist/jsx/ToastState.native.js.map +1 -0
- package/dist/jsx/ToastViewport.js +250 -0
- package/dist/jsx/ToastViewport.js.map +6 -0
- package/dist/jsx/ToastViewport.mjs +241 -0
- package/dist/jsx/ToastViewport.mjs.map +1 -0
- package/dist/jsx/ToastViewport.native.js +316 -0
- package/dist/jsx/ToastViewport.native.js.map +1 -0
- package/dist/jsx/Toaster.js +160 -0
- package/dist/jsx/Toaster.js.map +6 -0
- package/dist/jsx/Toaster.mjs +185 -0
- package/dist/jsx/Toaster.mjs.map +1 -0
- package/dist/jsx/Toaster.native.js +279 -0
- package/dist/jsx/Toaster.native.js.map +1 -0
- package/dist/jsx/constants.js +6 -0
- package/dist/jsx/constants.js.map +6 -0
- package/dist/jsx/constants.mjs +4 -0
- package/dist/jsx/constants.mjs.map +1 -0
- package/dist/jsx/constants.native.js +31 -0
- package/dist/jsx/constants.native.js.map +1 -0
- package/dist/jsx/createNativeToast.js +28 -0
- package/dist/jsx/createNativeToast.js.map +6 -0
- package/dist/jsx/createNativeToast.mjs +27 -0
- package/dist/jsx/createNativeToast.mjs.map +1 -0
- package/dist/jsx/createNativeToast.native.js +47 -0
- package/dist/jsx/createNativeToast.native.js.map +1 -0
- package/dist/jsx/index.js +7 -0
- package/dist/jsx/index.js.map +6 -0
- package/dist/jsx/index.mjs +4 -0
- package/dist/jsx/index.mjs.map +1 -0
- package/dist/jsx/index.native.js +31 -0
- package/dist/jsx/index.native.js.map +1 -0
- package/dist/jsx/types.js +1 -0
- package/dist/jsx/types.js.map +6 -0
- package/dist/jsx/types.mjs +2 -0
- package/dist/jsx/types.mjs.map +1 -0
- package/dist/jsx/types.native.js +19 -0
- package/dist/jsx/types.native.js.map +1 -0
- package/dist/jsx/useDragGesture.js +76 -0
- package/dist/jsx/useDragGesture.js.map +6 -0
- package/dist/jsx/useDragGesture.mjs +95 -0
- package/dist/jsx/useDragGesture.mjs.map +1 -0
- package/dist/jsx/useDragGesture.native.js +146 -0
- package/dist/jsx/useDragGesture.native.js.map +1 -0
- package/package.json +77 -0
- package/src/Toast.tsx +219 -0
- package/src/ToastAnnounce.tsx +102 -0
- package/src/ToastImperative.tsx +190 -0
- package/src/ToastImpl.tsx +503 -0
- package/src/ToastItem.tsx +694 -0
- package/src/ToastPortal.tsx +19 -0
- package/src/ToastProvider.tsx +197 -0
- package/src/ToastState.ts +397 -0
- package/src/ToastViewport.tsx +430 -0
- package/src/Toaster.tsx +445 -0
- package/src/constants.ts +2 -0
- package/src/createNativeToast.native.tsx +22 -0
- package/src/createNativeToast.tsx +48 -0
- package/src/index.ts +17 -0
- package/src/types.ts +71 -0
- package/src/useDragGesture.native.ts +199 -0
- package/src/useDragGesture.ts +218 -0
- package/types/Toast.d.ts +84 -0
- package/types/Toast.d.ts.map +1 -0
- package/types/ToastAnnounce.d.ts +18 -0
- package/types/ToastAnnounce.d.ts.map +1 -0
- package/types/ToastImperative.d.ts +95 -0
- package/types/ToastImperative.d.ts.map +1 -0
- package/types/ToastImpl.d.ts +109 -0
- package/types/ToastImpl.d.ts.map +1 -0
- package/types/ToastItem.d.ts +34 -0
- package/types/ToastItem.d.ts.map +1 -0
- package/types/ToastPortal.d.ts +8 -0
- package/types/ToastPortal.d.ts.map +1 -0
- package/types/ToastProvider.d.ts +92 -0
- package/types/ToastProvider.d.ts.map +1 -0
- package/types/ToastState.d.ts +177 -0
- package/types/ToastState.d.ts.map +1 -0
- package/types/ToastViewport.d.ts +75 -0
- package/types/ToastViewport.d.ts.map +1 -0
- package/types/Toaster.d.ts +120 -0
- package/types/Toaster.d.ts.map +1 -0
- package/types/constants.d.ts +3 -0
- package/types/constants.d.ts.map +1 -0
- package/types/createNativeToast.d.ts +4 -0
- package/types/createNativeToast.d.ts.map +1 -0
- package/types/createNativeToast.native.d.ts +4 -0
- package/types/createNativeToast.native.d.ts.map +1 -0
- package/types/index.d.ts +7 -0
- package/types/index.d.ts.map +1 -0
- package/types/types.d.ts +61 -0
- package/types/types.d.ts.map +1 -0
- package/types/useDragGesture.d.ts +32 -0
- package/types/useDragGesture.d.ts.map +1 -0
- package/types/useDragGesture.native.d.ts +26 -0
- package/types/useDragGesture.native.d.ts.map +1 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { createCollection } from '@tamagui/collection'
|
|
2
|
+
import type { NativeValue, TamaguiElement } from '@tamagui/core'
|
|
3
|
+
import { createStyledContext } from '@tamagui/core'
|
|
4
|
+
import { startTransition } from '@tamagui/start-transition'
|
|
5
|
+
import * as React from 'react'
|
|
6
|
+
|
|
7
|
+
import { TOAST_CONTEXT } from './constants'
|
|
8
|
+
import type { ToastImperativeOptions } from './ToastImperative'
|
|
9
|
+
import { ToastImperativeProvider } from './ToastImperative'
|
|
10
|
+
import type { BurntToastOptions } from './types'
|
|
11
|
+
|
|
12
|
+
/* -------------------------------------------------------------------------------------------------
|
|
13
|
+
* ToastProvider
|
|
14
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
15
|
+
|
|
16
|
+
const PROVIDER_NAME = 'ToastProvider'
|
|
17
|
+
|
|
18
|
+
const [Collection, useCollection] = createCollection<TamaguiElement>('Toast')
|
|
19
|
+
|
|
20
|
+
export type SwipeDirection = 'vertical' | 'up' | 'down' | 'horizontal' | 'left' | 'right'
|
|
21
|
+
|
|
22
|
+
export type ToastProviderContextValue = {
|
|
23
|
+
id: string
|
|
24
|
+
toastScope: string
|
|
25
|
+
label: string
|
|
26
|
+
duration: number
|
|
27
|
+
swipeDirection: SwipeDirection
|
|
28
|
+
swipeThreshold: number
|
|
29
|
+
toastCount: number
|
|
30
|
+
viewports: Record<string, TamaguiElement | null>
|
|
31
|
+
onViewportChange(name: string, viewport: TamaguiElement): void
|
|
32
|
+
onToastAdd(): void
|
|
33
|
+
onToastRemove(): void
|
|
34
|
+
isFocusedToastEscapeKeyDownRef: React.MutableRefObject<boolean>
|
|
35
|
+
isClosePausedRef: React.MutableRefObject<boolean>
|
|
36
|
+
options: ToastImperativeOptions
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type ToastScopes = string
|
|
40
|
+
|
|
41
|
+
type ScopedProps<P> = Omit<P, 'scope'> & { scope?: ToastScopes }
|
|
42
|
+
|
|
43
|
+
const { Provider: ToastProviderProvider, useStyledContext: useToastProviderContext } =
|
|
44
|
+
createStyledContext<ToastProviderContextValue>(
|
|
45
|
+
// since we always provide this we can avoid setting here
|
|
46
|
+
{} as ToastProviderContextValue,
|
|
47
|
+
'Toast__'
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
interface ToastProviderProps {
|
|
51
|
+
children?: React.ReactNode
|
|
52
|
+
/**
|
|
53
|
+
* An author-localized label for each toast. Used to help screen reader users
|
|
54
|
+
* associate the interruption with a toast.
|
|
55
|
+
* @defaultValue 'Notification'
|
|
56
|
+
*/
|
|
57
|
+
label?: string
|
|
58
|
+
/**
|
|
59
|
+
* Time in milliseconds that each toast should remain visible for.
|
|
60
|
+
* @defaultValue 5000
|
|
61
|
+
*/
|
|
62
|
+
duration?: number
|
|
63
|
+
/**
|
|
64
|
+
* Direction of pointer swipe that should close the toast.
|
|
65
|
+
* @defaultValue 'right'
|
|
66
|
+
*/
|
|
67
|
+
swipeDirection?: SwipeDirection
|
|
68
|
+
/**
|
|
69
|
+
* Distance in pixels that the swipe must pass before a close is triggered.
|
|
70
|
+
* @defaultValue 50
|
|
71
|
+
*/
|
|
72
|
+
swipeThreshold?: number
|
|
73
|
+
/**
|
|
74
|
+
* @defaultValue unique generated identifier
|
|
75
|
+
*/
|
|
76
|
+
id?: string
|
|
77
|
+
/**
|
|
78
|
+
* Will show a native toast if is true or is set to the current platform. On iOS, it wraps `SPIndicator` and `SPAlert`. On Android, it wraps `ToastAndroid`. On web, it wraps Notification API. Mobile's native features are handled by `burnt`.
|
|
79
|
+
* Only works with the imperative `useToast` hook.
|
|
80
|
+
*/
|
|
81
|
+
native?: NativeValue
|
|
82
|
+
/**
|
|
83
|
+
* Options for the burnt package if you're using native toasts on mobile
|
|
84
|
+
*/
|
|
85
|
+
burntOptions?: Omit<BurntToastOptions, 'title' | 'message' | 'duration'>
|
|
86
|
+
/**
|
|
87
|
+
* Options for the notification API if you're using native toasts on web
|
|
88
|
+
*/
|
|
89
|
+
notificationOptions?: NotificationOptions
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const ToastProvider: React.FC<ToastProviderProps> = (
|
|
93
|
+
props: ScopedProps<ToastProviderProps>
|
|
94
|
+
) => {
|
|
95
|
+
const {
|
|
96
|
+
scope = TOAST_CONTEXT,
|
|
97
|
+
id: providedId,
|
|
98
|
+
burntOptions,
|
|
99
|
+
native,
|
|
100
|
+
notificationOptions,
|
|
101
|
+
label = 'Notification',
|
|
102
|
+
duration = 5000,
|
|
103
|
+
swipeDirection = 'right',
|
|
104
|
+
swipeThreshold = 50,
|
|
105
|
+
children,
|
|
106
|
+
} = props
|
|
107
|
+
const backupId = React.useId()
|
|
108
|
+
const id = providedId ?? backupId
|
|
109
|
+
const [viewports, setViewports] = React.useState<
|
|
110
|
+
ToastProviderContextValue['viewports']
|
|
111
|
+
>({})
|
|
112
|
+
const [toastCount, setToastCount] = React.useState(0)
|
|
113
|
+
const isFocusedToastEscapeKeyDownRef = React.useRef(false)
|
|
114
|
+
const isClosePausedRef = React.useRef(false)
|
|
115
|
+
|
|
116
|
+
const handleViewportChange = React.useCallback(
|
|
117
|
+
(name: string, viewport: TamaguiElement | null) => {
|
|
118
|
+
startTransition(() => {
|
|
119
|
+
setViewports((prev) => ({ ...prev, [name]: viewport }))
|
|
120
|
+
})
|
|
121
|
+
},
|
|
122
|
+
[]
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
// memo context to avoid expensive re-renders
|
|
126
|
+
const options = React.useMemo(() => {
|
|
127
|
+
return {
|
|
128
|
+
duration,
|
|
129
|
+
burntOptions,
|
|
130
|
+
native,
|
|
131
|
+
notificationOptions,
|
|
132
|
+
}
|
|
133
|
+
// nested simple object so JSON.stringify
|
|
134
|
+
}, [JSON.stringify([duration, burntOptions, native, notificationOptions])])
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<Collection.Provider scope={scope}>
|
|
138
|
+
<ToastProviderProvider
|
|
139
|
+
scope={scope}
|
|
140
|
+
id={id}
|
|
141
|
+
label={label}
|
|
142
|
+
duration={duration}
|
|
143
|
+
swipeDirection={swipeDirection}
|
|
144
|
+
swipeThreshold={swipeThreshold}
|
|
145
|
+
toastCount={toastCount}
|
|
146
|
+
viewports={viewports}
|
|
147
|
+
onViewportChange={handleViewportChange}
|
|
148
|
+
onToastAdd={React.useCallback(() => {
|
|
149
|
+
startTransition(() => {
|
|
150
|
+
setToastCount((prevCount) => prevCount + 1)
|
|
151
|
+
})
|
|
152
|
+
}, [])}
|
|
153
|
+
onToastRemove={React.useCallback(() => {
|
|
154
|
+
startTransition(() => {
|
|
155
|
+
setToastCount((prevCount) => prevCount - 1)
|
|
156
|
+
})
|
|
157
|
+
}, [])}
|
|
158
|
+
isFocusedToastEscapeKeyDownRef={isFocusedToastEscapeKeyDownRef}
|
|
159
|
+
isClosePausedRef={isClosePausedRef}
|
|
160
|
+
options={options}
|
|
161
|
+
>
|
|
162
|
+
<ToastImperativeProvider options={options}>{children}</ToastImperativeProvider>
|
|
163
|
+
</ToastProviderProvider>
|
|
164
|
+
</Collection.Provider>
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function ReprogapateToastProvider(props: {
|
|
169
|
+
children: React.ReactNode
|
|
170
|
+
context: ToastProviderContextValue
|
|
171
|
+
}) {
|
|
172
|
+
const { children, context } = props
|
|
173
|
+
return (
|
|
174
|
+
<Collection.Provider scope={context.toastScope}>
|
|
175
|
+
<ToastProviderProvider {...context}>
|
|
176
|
+
<ToastImperativeProvider options={context.options}>
|
|
177
|
+
{children}
|
|
178
|
+
</ToastImperativeProvider>
|
|
179
|
+
</ToastProviderProvider>
|
|
180
|
+
</Collection.Provider>
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
ToastProvider.propTypes = {
|
|
185
|
+
label(props) {
|
|
186
|
+
if (props.label && typeof props.label === 'string' && !props.label.trim()) {
|
|
187
|
+
const error = `Invalid prop \`label\` supplied to \`${PROVIDER_NAME}\`. Expected non-empty \`string\`.`
|
|
188
|
+
return new Error(error)
|
|
189
|
+
}
|
|
190
|
+
return null
|
|
191
|
+
},
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
ToastProvider.displayName = PROVIDER_NAME
|
|
195
|
+
|
|
196
|
+
export { Collection, ToastProvider, useCollection, useToastProviderContext }
|
|
197
|
+
export type { ScopedProps, ToastProviderProps }
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
import type React from 'react'
|
|
2
|
+
import type { CreateNativeToastOptions, NativeToastRef } from './types'
|
|
3
|
+
|
|
4
|
+
// counter for generating unique toast ids
|
|
5
|
+
let toastsCounter = 1
|
|
6
|
+
|
|
7
|
+
export type ToastType = 'default' | 'success' | 'error' | 'warning' | 'info' | 'loading'
|
|
8
|
+
|
|
9
|
+
export interface ToastT {
|
|
10
|
+
id: string | number
|
|
11
|
+
title: React.ReactNode | (() => React.ReactNode)
|
|
12
|
+
description?: React.ReactNode | (() => React.ReactNode)
|
|
13
|
+
type?: ToastType
|
|
14
|
+
icon?: React.ReactNode
|
|
15
|
+
jsx?: React.ReactElement
|
|
16
|
+
dismissible?: boolean
|
|
17
|
+
duration?: number
|
|
18
|
+
promise?: PromiseT
|
|
19
|
+
action?: ToastAction
|
|
20
|
+
cancel?: ToastAction
|
|
21
|
+
onDismiss?: (toast: ToastT) => void
|
|
22
|
+
onAutoClose?: (toast: ToastT) => void
|
|
23
|
+
// internal
|
|
24
|
+
delete?: boolean
|
|
25
|
+
// style overrides
|
|
26
|
+
style?: React.CSSProperties
|
|
27
|
+
className?: string
|
|
28
|
+
// native options
|
|
29
|
+
burntOptions?: CreateNativeToastOptions['burntOptions']
|
|
30
|
+
notificationOptions?: CreateNativeToastOptions['notificationOptions']
|
|
31
|
+
// custom data users can add
|
|
32
|
+
[key: string]: any
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface ToastAction {
|
|
36
|
+
label: string
|
|
37
|
+
onClick?: (event: React.MouseEvent) => void
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface ToastToDismiss {
|
|
41
|
+
id: string | number
|
|
42
|
+
dismiss: true
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type ExternalToast = Omit<
|
|
46
|
+
ToastT,
|
|
47
|
+
'id' | 'title' | 'type' | 'delete' | 'promise'
|
|
48
|
+
> & {
|
|
49
|
+
id?: string | number
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type PromiseT<Data = any> = Promise<Data> | (() => Promise<Data>)
|
|
53
|
+
|
|
54
|
+
export interface PromiseData<Data = any> {
|
|
55
|
+
loading?: React.ReactNode | (() => React.ReactNode)
|
|
56
|
+
success?: React.ReactNode | ((data: Data) => React.ReactNode)
|
|
57
|
+
error?: React.ReactNode | ((error: any) => React.ReactNode)
|
|
58
|
+
description?: React.ReactNode | ((data: any) => React.ReactNode)
|
|
59
|
+
finally?: () => void
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
type TitleT = React.ReactNode | (() => React.ReactNode)
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Observer class that manages toast state globally.
|
|
66
|
+
* Follows the pattern from Sonner for a clean, decoupled architecture.
|
|
67
|
+
*/
|
|
68
|
+
class Observer {
|
|
69
|
+
subscribers: Array<(toast: ToastT | ToastToDismiss) => void> = []
|
|
70
|
+
toasts: ToastT[] = []
|
|
71
|
+
dismissedToasts: Set<string | number> = new Set()
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Subscribe to toast state changes.
|
|
75
|
+
* Returns an unsubscribe function.
|
|
76
|
+
*/
|
|
77
|
+
subscribe = (subscriber: (toast: ToastT | ToastToDismiss) => void) => {
|
|
78
|
+
this.subscribers.push(subscriber)
|
|
79
|
+
|
|
80
|
+
return () => {
|
|
81
|
+
const index = this.subscribers.indexOf(subscriber)
|
|
82
|
+
if (index > -1) {
|
|
83
|
+
this.subscribers.splice(index, 1)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Publish a toast to all subscribers
|
|
90
|
+
*/
|
|
91
|
+
publish = (data: ToastT) => {
|
|
92
|
+
this.subscribers.forEach((subscriber) => subscriber(data))
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Add a new toast to the internal array and publish to subscribers
|
|
97
|
+
*/
|
|
98
|
+
addToast = (data: ToastT) => {
|
|
99
|
+
this.publish(data)
|
|
100
|
+
this.toasts = [...this.toasts, data]
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Create or update a toast
|
|
105
|
+
*/
|
|
106
|
+
create = (
|
|
107
|
+
data: ExternalToast & {
|
|
108
|
+
title?: TitleT
|
|
109
|
+
type?: ToastType
|
|
110
|
+
promise?: PromiseT
|
|
111
|
+
jsx?: React.ReactElement
|
|
112
|
+
}
|
|
113
|
+
) => {
|
|
114
|
+
const { title, ...rest } = data
|
|
115
|
+
const id =
|
|
116
|
+
typeof data?.id === 'number' || (typeof data?.id === 'string' && data.id.length > 0)
|
|
117
|
+
? data.id
|
|
118
|
+
: toastsCounter++
|
|
119
|
+
|
|
120
|
+
const alreadyExists = this.toasts.find((toast) => toast.id === id)
|
|
121
|
+
const dismissible = data.dismissible ?? true
|
|
122
|
+
|
|
123
|
+
// if this toast was previously dismissed, clear that
|
|
124
|
+
if (this.dismissedToasts.has(id)) {
|
|
125
|
+
this.dismissedToasts.delete(id)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (alreadyExists) {
|
|
129
|
+
// update existing toast
|
|
130
|
+
this.toasts = this.toasts.map((toast) => {
|
|
131
|
+
if (toast.id === id) {
|
|
132
|
+
this.publish({ ...toast, ...data, id, title, dismissible })
|
|
133
|
+
return { ...toast, ...data, id, title, dismissible }
|
|
134
|
+
}
|
|
135
|
+
return toast
|
|
136
|
+
})
|
|
137
|
+
} else {
|
|
138
|
+
this.addToast({ title, ...rest, dismissible, id } as ToastT)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return id
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Dismiss a toast by id, or all toasts if no id provided
|
|
146
|
+
*/
|
|
147
|
+
dismiss = (id?: string | number) => {
|
|
148
|
+
if (id !== undefined) {
|
|
149
|
+
this.dismissedToasts.add(id)
|
|
150
|
+
// use requestAnimationFrame to batch updates
|
|
151
|
+
requestAnimationFrame(() => {
|
|
152
|
+
this.subscribers.forEach((subscriber) => subscriber({ id, dismiss: true }))
|
|
153
|
+
})
|
|
154
|
+
} else {
|
|
155
|
+
// dismiss all
|
|
156
|
+
this.toasts.forEach((toast) => {
|
|
157
|
+
this.subscribers.forEach((subscriber) =>
|
|
158
|
+
subscriber({ id: toast.id, dismiss: true })
|
|
159
|
+
)
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return id
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Show a basic toast message
|
|
168
|
+
*/
|
|
169
|
+
message = (title: TitleT, data?: ExternalToast) => {
|
|
170
|
+
return this.create({ ...data, title, type: 'default' })
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Show a success toast
|
|
175
|
+
*/
|
|
176
|
+
success = (title: TitleT, data?: ExternalToast) => {
|
|
177
|
+
return this.create({ ...data, title, type: 'success' })
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Show an error toast
|
|
182
|
+
*/
|
|
183
|
+
error = (title: TitleT, data?: ExternalToast) => {
|
|
184
|
+
return this.create({ ...data, title, type: 'error' })
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Show a warning toast
|
|
189
|
+
*/
|
|
190
|
+
warning = (title: TitleT, data?: ExternalToast) => {
|
|
191
|
+
return this.create({ ...data, title, type: 'warning' })
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Show an info toast
|
|
196
|
+
*/
|
|
197
|
+
info = (title: TitleT, data?: ExternalToast) => {
|
|
198
|
+
return this.create({ ...data, title, type: 'info' })
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Show a loading toast
|
|
203
|
+
*/
|
|
204
|
+
loading = (title: TitleT, data?: ExternalToast) => {
|
|
205
|
+
return this.create({ ...data, title, type: 'loading' })
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Show a toast for a promise, automatically transitioning through
|
|
210
|
+
* loading -> success/error states
|
|
211
|
+
*/
|
|
212
|
+
promise = <ToastData>(promise: PromiseT<ToastData>, data?: PromiseData<ToastData>) => {
|
|
213
|
+
if (!data) {
|
|
214
|
+
return
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
let id: string | number | undefined = undefined
|
|
218
|
+
|
|
219
|
+
// show loading state if provided
|
|
220
|
+
if (data.loading !== undefined) {
|
|
221
|
+
id = this.create({
|
|
222
|
+
promise,
|
|
223
|
+
type: 'loading',
|
|
224
|
+
title: data.loading,
|
|
225
|
+
description:
|
|
226
|
+
typeof data.description !== 'function' ? data.description : undefined,
|
|
227
|
+
// loading toasts shouldn't auto-dismiss
|
|
228
|
+
duration: Number.POSITIVE_INFINITY,
|
|
229
|
+
})
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const p = Promise.resolve(promise instanceof Function ? promise() : promise)
|
|
233
|
+
|
|
234
|
+
let shouldDismiss = id !== undefined
|
|
235
|
+
let result: ['resolve', ToastData] | ['reject', unknown]
|
|
236
|
+
|
|
237
|
+
const originalPromise = p
|
|
238
|
+
.then(async (response) => {
|
|
239
|
+
result = ['resolve', response]
|
|
240
|
+
|
|
241
|
+
// check if response is an HTTP error
|
|
242
|
+
if (isHttpResponse(response) && !response.ok) {
|
|
243
|
+
shouldDismiss = false
|
|
244
|
+
const errorMsg =
|
|
245
|
+
typeof data.error === 'function'
|
|
246
|
+
? await data.error(`HTTP error! status: ${response.status}`)
|
|
247
|
+
: data.error
|
|
248
|
+
const description =
|
|
249
|
+
typeof data.description === 'function'
|
|
250
|
+
? await data.description(`HTTP error! status: ${response.status}`)
|
|
251
|
+
: data.description
|
|
252
|
+
|
|
253
|
+
this.create({ id, type: 'error', title: errorMsg, description })
|
|
254
|
+
} else if (data.success !== undefined) {
|
|
255
|
+
shouldDismiss = false
|
|
256
|
+
const successMsg =
|
|
257
|
+
typeof data.success === 'function'
|
|
258
|
+
? await data.success(response)
|
|
259
|
+
: data.success
|
|
260
|
+
const description =
|
|
261
|
+
typeof data.description === 'function'
|
|
262
|
+
? await data.description(response)
|
|
263
|
+
: data.description
|
|
264
|
+
|
|
265
|
+
this.create({ id, type: 'success', title: successMsg, description })
|
|
266
|
+
}
|
|
267
|
+
})
|
|
268
|
+
.catch(async (error) => {
|
|
269
|
+
result = ['reject', error]
|
|
270
|
+
|
|
271
|
+
if (data.error !== undefined) {
|
|
272
|
+
shouldDismiss = false
|
|
273
|
+
const errorMsg =
|
|
274
|
+
typeof data.error === 'function' ? await data.error(error) : data.error
|
|
275
|
+
const description =
|
|
276
|
+
typeof data.description === 'function'
|
|
277
|
+
? await data.description(error)
|
|
278
|
+
: data.description
|
|
279
|
+
|
|
280
|
+
this.create({ id, type: 'error', title: errorMsg, description })
|
|
281
|
+
}
|
|
282
|
+
})
|
|
283
|
+
.finally(() => {
|
|
284
|
+
if (shouldDismiss) {
|
|
285
|
+
// toast is still in load state, dismiss it
|
|
286
|
+
this.dismiss(id)
|
|
287
|
+
id = undefined
|
|
288
|
+
}
|
|
289
|
+
data.finally?.()
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
// return a promise that can be unwrapped
|
|
293
|
+
const unwrap = () =>
|
|
294
|
+
new Promise<ToastData>((resolve, reject) =>
|
|
295
|
+
originalPromise
|
|
296
|
+
.then(() => (result[0] === 'reject' ? reject(result[1]) : resolve(result[1])))
|
|
297
|
+
.catch(reject)
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
if (typeof id !== 'string' && typeof id !== 'number') {
|
|
301
|
+
return { unwrap }
|
|
302
|
+
} else {
|
|
303
|
+
return Object.assign(id, { unwrap })
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Show a custom JSX toast
|
|
309
|
+
*/
|
|
310
|
+
custom = (jsx: (id: string | number) => React.ReactElement, data?: ExternalToast) => {
|
|
311
|
+
const id = data?.id ?? toastsCounter++
|
|
312
|
+
this.create({ jsx: jsx(id), ...data, id })
|
|
313
|
+
return id
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Get all active (non-dismissed) toasts
|
|
318
|
+
*/
|
|
319
|
+
getActiveToasts = () => {
|
|
320
|
+
return this.toasts.filter((toast) => !this.dismissedToasts.has(toast.id))
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Get full toast history
|
|
325
|
+
*/
|
|
326
|
+
getHistory = () => {
|
|
327
|
+
return this.toasts
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function isHttpResponse(data: any): data is Response {
|
|
332
|
+
return (
|
|
333
|
+
data &&
|
|
334
|
+
typeof data === 'object' &&
|
|
335
|
+
'ok' in data &&
|
|
336
|
+
typeof data.ok === 'boolean' &&
|
|
337
|
+
'status' in data &&
|
|
338
|
+
typeof data.status === 'number'
|
|
339
|
+
)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// singleton instance
|
|
343
|
+
export const ToastState = new Observer()
|
|
344
|
+
|
|
345
|
+
// basic toast function
|
|
346
|
+
const toastFunction = (title: TitleT, data?: ExternalToast) => {
|
|
347
|
+
return ToastState.create({ ...data, title })
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// getters
|
|
351
|
+
const getHistory = () => ToastState.getHistory()
|
|
352
|
+
const getToasts = () => ToastState.getActiveToasts()
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Main toast API - call directly or use methods like toast.success()
|
|
356
|
+
*
|
|
357
|
+
* @example
|
|
358
|
+
* // basic usage
|
|
359
|
+
* toast('Hello world')
|
|
360
|
+
*
|
|
361
|
+
* // with type
|
|
362
|
+
* toast.success('Saved!')
|
|
363
|
+
* toast.error('Something went wrong')
|
|
364
|
+
*
|
|
365
|
+
* // with options
|
|
366
|
+
* toast('Hello', { duration: 5000 })
|
|
367
|
+
*
|
|
368
|
+
* // promise toast
|
|
369
|
+
* toast.promise(fetch('/api'), {
|
|
370
|
+
* loading: 'Loading...',
|
|
371
|
+
* success: 'Done!',
|
|
372
|
+
* error: 'Failed'
|
|
373
|
+
* })
|
|
374
|
+
*
|
|
375
|
+
* // custom JSX
|
|
376
|
+
* toast.custom((id) => <MyToast id={id} />)
|
|
377
|
+
*
|
|
378
|
+
* // dismiss
|
|
379
|
+
* const id = toast('Hello')
|
|
380
|
+
* toast.dismiss(id)
|
|
381
|
+
* toast.dismiss() // dismiss all
|
|
382
|
+
*/
|
|
383
|
+
export const toast = Object.assign(toastFunction, {
|
|
384
|
+
success: ToastState.success,
|
|
385
|
+
error: ToastState.error,
|
|
386
|
+
warning: ToastState.warning,
|
|
387
|
+
info: ToastState.info,
|
|
388
|
+
loading: ToastState.loading,
|
|
389
|
+
promise: ToastState.promise,
|
|
390
|
+
custom: ToastState.custom,
|
|
391
|
+
dismiss: ToastState.dismiss,
|
|
392
|
+
message: ToastState.message,
|
|
393
|
+
getHistory,
|
|
394
|
+
getToasts,
|
|
395
|
+
})
|
|
396
|
+
|
|
397
|
+
export type { NativeToastRef }
|