@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.
Files changed (307) hide show
  1. package/dist/cjs/Toast.cjs +170 -0
  2. package/dist/cjs/Toast.js +119 -0
  3. package/dist/cjs/Toast.js.map +6 -0
  4. package/dist/cjs/Toast.native.js +174 -0
  5. package/dist/cjs/Toast.native.js.map +1 -0
  6. package/dist/cjs/ToastAnnounce.cjs +97 -0
  7. package/dist/cjs/ToastAnnounce.js +72 -0
  8. package/dist/cjs/ToastAnnounce.js.map +6 -0
  9. package/dist/cjs/ToastAnnounce.native.js +105 -0
  10. package/dist/cjs/ToastAnnounce.native.js.map +1 -0
  11. package/dist/cjs/ToastImperative.cjs +100 -0
  12. package/dist/cjs/ToastImperative.js +71 -0
  13. package/dist/cjs/ToastImperative.js.map +6 -0
  14. package/dist/cjs/ToastImperative.native.js +122 -0
  15. package/dist/cjs/ToastImperative.native.js.map +1 -0
  16. package/dist/cjs/ToastImpl.cjs +292 -0
  17. package/dist/cjs/ToastImpl.js +227 -0
  18. package/dist/cjs/ToastImpl.js.map +6 -0
  19. package/dist/cjs/ToastImpl.native.js +327 -0
  20. package/dist/cjs/ToastImpl.native.js.map +1 -0
  21. package/dist/cjs/ToastItem.cjs +466 -0
  22. package/dist/cjs/ToastItem.js +356 -0
  23. package/dist/cjs/ToastItem.js.map +6 -0
  24. package/dist/cjs/ToastItem.native.js +547 -0
  25. package/dist/cjs/ToastItem.native.js.map +1 -0
  26. package/dist/cjs/ToastPortal.cjs +44 -0
  27. package/dist/cjs/ToastPortal.js +26 -0
  28. package/dist/cjs/ToastPortal.js.map +6 -0
  29. package/dist/cjs/ToastPortal.native.js +47 -0
  30. package/dist/cjs/ToastPortal.native.js.map +1 -0
  31. package/dist/cjs/ToastProvider.cjs +146 -0
  32. package/dist/cjs/ToastProvider.js +105 -0
  33. package/dist/cjs/ToastProvider.js.map +6 -0
  34. package/dist/cjs/ToastProvider.native.js +159 -0
  35. package/dist/cjs/ToastProvider.native.js.map +1 -0
  36. package/dist/cjs/ToastState.cjs +248 -0
  37. package/dist/cjs/ToastState.js +160 -0
  38. package/dist/cjs/ToastState.js.map +6 -0
  39. package/dist/cjs/ToastState.native.js +257 -0
  40. package/dist/cjs/ToastState.native.js.map +1 -0
  41. package/dist/cjs/ToastViewport.cjs +278 -0
  42. package/dist/cjs/ToastViewport.js +263 -0
  43. package/dist/cjs/ToastViewport.js.map +6 -0
  44. package/dist/cjs/ToastViewport.native.js +316 -0
  45. package/dist/cjs/ToastViewport.native.js.map +1 -0
  46. package/dist/cjs/Toaster.cjs +219 -0
  47. package/dist/cjs/Toaster.js +177 -0
  48. package/dist/cjs/Toaster.js.map +6 -0
  49. package/dist/cjs/Toaster.native.js +279 -0
  50. package/dist/cjs/Toaster.native.js.map +1 -0
  51. package/dist/cjs/constants.cjs +28 -0
  52. package/dist/cjs/constants.js +22 -0
  53. package/dist/cjs/constants.js.map +6 -0
  54. package/dist/cjs/constants.native.js +31 -0
  55. package/dist/cjs/constants.native.js.map +1 -0
  56. package/dist/cjs/createNativeToast.cjs +51 -0
  57. package/dist/cjs/createNativeToast.js +44 -0
  58. package/dist/cjs/createNativeToast.js.map +6 -0
  59. package/dist/cjs/createNativeToast.native.js +47 -0
  60. package/dist/cjs/createNativeToast.native.js.map +1 -0
  61. package/dist/cjs/index.cjs +28 -0
  62. package/dist/cjs/index.js +22 -0
  63. package/dist/cjs/index.js.map +6 -0
  64. package/dist/cjs/index.native.js +31 -0
  65. package/dist/cjs/index.native.js.map +1 -0
  66. package/dist/cjs/types.cjs +16 -0
  67. package/dist/cjs/types.js +14 -0
  68. package/dist/cjs/types.js.map +6 -0
  69. package/dist/cjs/types.native.js +19 -0
  70. package/dist/cjs/types.native.js.map +1 -0
  71. package/dist/cjs/useDragGesture.cjs +129 -0
  72. package/dist/cjs/useDragGesture.js +100 -0
  73. package/dist/cjs/useDragGesture.js.map +6 -0
  74. package/dist/cjs/useDragGesture.native.js +146 -0
  75. package/dist/cjs/useDragGesture.native.js.map +1 -0
  76. package/dist/esm/Toast.js +107 -0
  77. package/dist/esm/Toast.js.map +6 -0
  78. package/dist/esm/Toast.mjs +131 -0
  79. package/dist/esm/Toast.mjs.map +1 -0
  80. package/dist/esm/Toast.native.js +132 -0
  81. package/dist/esm/Toast.native.js.map +1 -0
  82. package/dist/esm/ToastAnnounce.js +55 -0
  83. package/dist/esm/ToastAnnounce.js.map +6 -0
  84. package/dist/esm/ToastAnnounce.mjs +62 -0
  85. package/dist/esm/ToastAnnounce.mjs.map +1 -0
  86. package/dist/esm/ToastAnnounce.native.js +67 -0
  87. package/dist/esm/ToastAnnounce.native.js.map +1 -0
  88. package/dist/esm/ToastImperative.js +50 -0
  89. package/dist/esm/ToastImperative.js.map +6 -0
  90. package/dist/esm/ToastImperative.mjs +63 -0
  91. package/dist/esm/ToastImperative.mjs.map +1 -0
  92. package/dist/esm/ToastImperative.native.js +82 -0
  93. package/dist/esm/ToastImperative.native.js.map +1 -0
  94. package/dist/esm/ToastImpl.js +225 -0
  95. package/dist/esm/ToastImpl.js.map +6 -0
  96. package/dist/esm/ToastImpl.mjs +256 -0
  97. package/dist/esm/ToastImpl.mjs.map +1 -0
  98. package/dist/esm/ToastImpl.native.js +288 -0
  99. package/dist/esm/ToastImpl.native.js.map +1 -0
  100. package/dist/esm/ToastItem.js +339 -0
  101. package/dist/esm/ToastItem.js.map +6 -0
  102. package/dist/esm/ToastItem.mjs +432 -0
  103. package/dist/esm/ToastItem.mjs.map +1 -0
  104. package/dist/esm/ToastItem.native.js +510 -0
  105. package/dist/esm/ToastItem.native.js.map +1 -0
  106. package/dist/esm/ToastPortal.js +13 -0
  107. package/dist/esm/ToastPortal.js.map +6 -0
  108. package/dist/esm/ToastPortal.mjs +21 -0
  109. package/dist/esm/ToastPortal.mjs.map +1 -0
  110. package/dist/esm/ToastPortal.native.js +21 -0
  111. package/dist/esm/ToastPortal.native.js.map +1 -0
  112. package/dist/esm/ToastProvider.js +87 -0
  113. package/dist/esm/ToastProvider.js.map +6 -0
  114. package/dist/esm/ToastProvider.mjs +108 -0
  115. package/dist/esm/ToastProvider.mjs.map +1 -0
  116. package/dist/esm/ToastProvider.native.js +118 -0
  117. package/dist/esm/ToastProvider.native.js.map +1 -0
  118. package/dist/esm/ToastState.js +144 -0
  119. package/dist/esm/ToastState.js.map +6 -0
  120. package/dist/esm/ToastState.mjs +224 -0
  121. package/dist/esm/ToastState.mjs.map +1 -0
  122. package/dist/esm/ToastState.native.js +230 -0
  123. package/dist/esm/ToastState.native.js.map +1 -0
  124. package/dist/esm/ToastViewport.js +250 -0
  125. package/dist/esm/ToastViewport.js.map +6 -0
  126. package/dist/esm/ToastViewport.mjs +241 -0
  127. package/dist/esm/ToastViewport.mjs.map +1 -0
  128. package/dist/esm/ToastViewport.native.js +276 -0
  129. package/dist/esm/ToastViewport.native.js.map +1 -0
  130. package/dist/esm/Toaster.js +160 -0
  131. package/dist/esm/Toaster.js.map +6 -0
  132. package/dist/esm/Toaster.mjs +185 -0
  133. package/dist/esm/Toaster.mjs.map +1 -0
  134. package/dist/esm/Toaster.native.js +242 -0
  135. package/dist/esm/Toaster.native.js.map +1 -0
  136. package/dist/esm/constants.js +6 -0
  137. package/dist/esm/constants.js.map +6 -0
  138. package/dist/esm/constants.mjs +4 -0
  139. package/dist/esm/constants.mjs.map +1 -0
  140. package/dist/esm/constants.native.js +4 -0
  141. package/dist/esm/constants.native.js.map +1 -0
  142. package/dist/esm/createNativeToast.js +28 -0
  143. package/dist/esm/createNativeToast.js.map +6 -0
  144. package/dist/esm/createNativeToast.mjs +27 -0
  145. package/dist/esm/createNativeToast.mjs.map +1 -0
  146. package/dist/esm/createNativeToast.native.js +20 -0
  147. package/dist/esm/createNativeToast.native.js.map +1 -0
  148. package/dist/esm/index.js +7 -0
  149. package/dist/esm/index.js.map +6 -0
  150. package/dist/esm/index.mjs +4 -0
  151. package/dist/esm/index.mjs.map +1 -0
  152. package/dist/esm/index.native.js +4 -0
  153. package/dist/esm/index.native.js.map +1 -0
  154. package/dist/esm/types.js +1 -0
  155. package/dist/esm/types.js.map +6 -0
  156. package/dist/esm/types.mjs +2 -0
  157. package/dist/esm/types.mjs.map +1 -0
  158. package/dist/esm/types.native.js +2 -0
  159. package/dist/esm/types.native.js.map +1 -0
  160. package/dist/esm/useDragGesture.js +76 -0
  161. package/dist/esm/useDragGesture.js.map +6 -0
  162. package/dist/esm/useDragGesture.mjs +95 -0
  163. package/dist/esm/useDragGesture.mjs.map +1 -0
  164. package/dist/esm/useDragGesture.native.js +109 -0
  165. package/dist/esm/useDragGesture.native.js.map +1 -0
  166. package/dist/jsx/Toast.js +107 -0
  167. package/dist/jsx/Toast.js.map +6 -0
  168. package/dist/jsx/Toast.mjs +131 -0
  169. package/dist/jsx/Toast.mjs.map +1 -0
  170. package/dist/jsx/Toast.native.js +174 -0
  171. package/dist/jsx/Toast.native.js.map +1 -0
  172. package/dist/jsx/ToastAnnounce.js +55 -0
  173. package/dist/jsx/ToastAnnounce.js.map +6 -0
  174. package/dist/jsx/ToastAnnounce.mjs +62 -0
  175. package/dist/jsx/ToastAnnounce.mjs.map +1 -0
  176. package/dist/jsx/ToastAnnounce.native.js +105 -0
  177. package/dist/jsx/ToastAnnounce.native.js.map +1 -0
  178. package/dist/jsx/ToastImperative.js +50 -0
  179. package/dist/jsx/ToastImperative.js.map +6 -0
  180. package/dist/jsx/ToastImperative.mjs +63 -0
  181. package/dist/jsx/ToastImperative.mjs.map +1 -0
  182. package/dist/jsx/ToastImperative.native.js +122 -0
  183. package/dist/jsx/ToastImperative.native.js.map +1 -0
  184. package/dist/jsx/ToastImpl.js +225 -0
  185. package/dist/jsx/ToastImpl.js.map +6 -0
  186. package/dist/jsx/ToastImpl.mjs +256 -0
  187. package/dist/jsx/ToastImpl.mjs.map +1 -0
  188. package/dist/jsx/ToastImpl.native.js +327 -0
  189. package/dist/jsx/ToastImpl.native.js.map +1 -0
  190. package/dist/jsx/ToastItem.js +339 -0
  191. package/dist/jsx/ToastItem.js.map +6 -0
  192. package/dist/jsx/ToastItem.mjs +432 -0
  193. package/dist/jsx/ToastItem.mjs.map +1 -0
  194. package/dist/jsx/ToastItem.native.js +547 -0
  195. package/dist/jsx/ToastItem.native.js.map +1 -0
  196. package/dist/jsx/ToastPortal.js +13 -0
  197. package/dist/jsx/ToastPortal.js.map +6 -0
  198. package/dist/jsx/ToastPortal.mjs +21 -0
  199. package/dist/jsx/ToastPortal.mjs.map +1 -0
  200. package/dist/jsx/ToastPortal.native.js +47 -0
  201. package/dist/jsx/ToastPortal.native.js.map +1 -0
  202. package/dist/jsx/ToastProvider.js +87 -0
  203. package/dist/jsx/ToastProvider.js.map +6 -0
  204. package/dist/jsx/ToastProvider.mjs +108 -0
  205. package/dist/jsx/ToastProvider.mjs.map +1 -0
  206. package/dist/jsx/ToastProvider.native.js +159 -0
  207. package/dist/jsx/ToastProvider.native.js.map +1 -0
  208. package/dist/jsx/ToastState.js +144 -0
  209. package/dist/jsx/ToastState.js.map +6 -0
  210. package/dist/jsx/ToastState.mjs +224 -0
  211. package/dist/jsx/ToastState.mjs.map +1 -0
  212. package/dist/jsx/ToastState.native.js +257 -0
  213. package/dist/jsx/ToastState.native.js.map +1 -0
  214. package/dist/jsx/ToastViewport.js +250 -0
  215. package/dist/jsx/ToastViewport.js.map +6 -0
  216. package/dist/jsx/ToastViewport.mjs +241 -0
  217. package/dist/jsx/ToastViewport.mjs.map +1 -0
  218. package/dist/jsx/ToastViewport.native.js +316 -0
  219. package/dist/jsx/ToastViewport.native.js.map +1 -0
  220. package/dist/jsx/Toaster.js +160 -0
  221. package/dist/jsx/Toaster.js.map +6 -0
  222. package/dist/jsx/Toaster.mjs +185 -0
  223. package/dist/jsx/Toaster.mjs.map +1 -0
  224. package/dist/jsx/Toaster.native.js +279 -0
  225. package/dist/jsx/Toaster.native.js.map +1 -0
  226. package/dist/jsx/constants.js +6 -0
  227. package/dist/jsx/constants.js.map +6 -0
  228. package/dist/jsx/constants.mjs +4 -0
  229. package/dist/jsx/constants.mjs.map +1 -0
  230. package/dist/jsx/constants.native.js +31 -0
  231. package/dist/jsx/constants.native.js.map +1 -0
  232. package/dist/jsx/createNativeToast.js +28 -0
  233. package/dist/jsx/createNativeToast.js.map +6 -0
  234. package/dist/jsx/createNativeToast.mjs +27 -0
  235. package/dist/jsx/createNativeToast.mjs.map +1 -0
  236. package/dist/jsx/createNativeToast.native.js +47 -0
  237. package/dist/jsx/createNativeToast.native.js.map +1 -0
  238. package/dist/jsx/index.js +7 -0
  239. package/dist/jsx/index.js.map +6 -0
  240. package/dist/jsx/index.mjs +4 -0
  241. package/dist/jsx/index.mjs.map +1 -0
  242. package/dist/jsx/index.native.js +31 -0
  243. package/dist/jsx/index.native.js.map +1 -0
  244. package/dist/jsx/types.js +1 -0
  245. package/dist/jsx/types.js.map +6 -0
  246. package/dist/jsx/types.mjs +2 -0
  247. package/dist/jsx/types.mjs.map +1 -0
  248. package/dist/jsx/types.native.js +19 -0
  249. package/dist/jsx/types.native.js.map +1 -0
  250. package/dist/jsx/useDragGesture.js +76 -0
  251. package/dist/jsx/useDragGesture.js.map +6 -0
  252. package/dist/jsx/useDragGesture.mjs +95 -0
  253. package/dist/jsx/useDragGesture.mjs.map +1 -0
  254. package/dist/jsx/useDragGesture.native.js +146 -0
  255. package/dist/jsx/useDragGesture.native.js.map +1 -0
  256. package/package.json +77 -0
  257. package/src/Toast.tsx +219 -0
  258. package/src/ToastAnnounce.tsx +102 -0
  259. package/src/ToastImperative.tsx +190 -0
  260. package/src/ToastImpl.tsx +503 -0
  261. package/src/ToastItem.tsx +694 -0
  262. package/src/ToastPortal.tsx +19 -0
  263. package/src/ToastProvider.tsx +197 -0
  264. package/src/ToastState.ts +397 -0
  265. package/src/ToastViewport.tsx +430 -0
  266. package/src/Toaster.tsx +445 -0
  267. package/src/constants.ts +2 -0
  268. package/src/createNativeToast.native.tsx +22 -0
  269. package/src/createNativeToast.tsx +48 -0
  270. package/src/index.ts +17 -0
  271. package/src/types.ts +71 -0
  272. package/src/useDragGesture.native.ts +199 -0
  273. package/src/useDragGesture.ts +218 -0
  274. package/types/Toast.d.ts +84 -0
  275. package/types/Toast.d.ts.map +1 -0
  276. package/types/ToastAnnounce.d.ts +18 -0
  277. package/types/ToastAnnounce.d.ts.map +1 -0
  278. package/types/ToastImperative.d.ts +95 -0
  279. package/types/ToastImperative.d.ts.map +1 -0
  280. package/types/ToastImpl.d.ts +109 -0
  281. package/types/ToastImpl.d.ts.map +1 -0
  282. package/types/ToastItem.d.ts +34 -0
  283. package/types/ToastItem.d.ts.map +1 -0
  284. package/types/ToastPortal.d.ts +8 -0
  285. package/types/ToastPortal.d.ts.map +1 -0
  286. package/types/ToastProvider.d.ts +92 -0
  287. package/types/ToastProvider.d.ts.map +1 -0
  288. package/types/ToastState.d.ts +177 -0
  289. package/types/ToastState.d.ts.map +1 -0
  290. package/types/ToastViewport.d.ts +75 -0
  291. package/types/ToastViewport.d.ts.map +1 -0
  292. package/types/Toaster.d.ts +120 -0
  293. package/types/Toaster.d.ts.map +1 -0
  294. package/types/constants.d.ts +3 -0
  295. package/types/constants.d.ts.map +1 -0
  296. package/types/createNativeToast.d.ts +4 -0
  297. package/types/createNativeToast.d.ts.map +1 -0
  298. package/types/createNativeToast.native.d.ts +4 -0
  299. package/types/createNativeToast.native.d.ts.map +1 -0
  300. package/types/index.d.ts +7 -0
  301. package/types/index.d.ts.map +1 -0
  302. package/types/types.d.ts +61 -0
  303. package/types/types.d.ts.map +1 -0
  304. package/types/useDragGesture.d.ts +32 -0
  305. package/types/useDragGesture.d.ts.map +1 -0
  306. package/types/useDragGesture.native.d.ts +26 -0
  307. 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 }