@tamagui/toast 2.7.7 → 3.0.0-beta.643.1

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 (303) hide show
  1. package/dist/cjs/ToastComposable.cjs +773 -882
  2. package/dist/cjs/ToastComposable.native.cjs +924 -0
  3. package/dist/cjs/ToastComposable.native.js +854 -966
  4. package/dist/cjs/ToastComposable.native.js.map +1 -1
  5. package/dist/cjs/ToastItemFrame.cjs +48 -119
  6. package/dist/cjs/ToastItemFrame.native.cjs +76 -0
  7. package/dist/cjs/ToastItemFrame.native.js +50 -120
  8. package/dist/cjs/ToastItemFrame.native.js.map +1 -1
  9. package/dist/cjs/ToastState.cjs +281 -290
  10. package/dist/cjs/ToastState.native.cjs +312 -0
  11. package/dist/cjs/ToastState.native.js +280 -290
  12. package/dist/cjs/ToastState.native.js.map +1 -1
  13. package/dist/cjs/createNativeToast.cjs +38 -44
  14. package/dist/cjs/createNativeToast.native.cjs +54 -0
  15. package/dist/cjs/createNativeToast.native.js +36 -40
  16. package/dist/cjs/createNativeToast.native.js.map +1 -1
  17. package/dist/cjs/dispatchNativeToast.cjs +32 -35
  18. package/dist/cjs/dispatchNativeToast.native.cjs +48 -0
  19. package/dist/cjs/dispatchNativeToast.native.js +34 -36
  20. package/dist/cjs/dispatchNativeToast.native.js.map +1 -1
  21. package/dist/cjs/index.cjs +25 -12
  22. package/dist/cjs/index.native.cjs +35 -0
  23. package/dist/cjs/index.native.js +26 -12
  24. package/dist/cjs/index.native.js.map +1 -1
  25. package/dist/cjs/types.cjs +10 -11
  26. package/dist/cjs/types.native.cjs +19 -0
  27. package/dist/cjs/types.native.js +10 -10
  28. package/dist/cjs/types.native.js.map +1 -1
  29. package/dist/cjs/useAnimatedDragGesture.cjs +197 -194
  30. package/dist/cjs/useAnimatedDragGesture.native.cjs +210 -0
  31. package/dist/cjs/useAnimatedDragGesture.native.js +179 -191
  32. package/dist/cjs/useAnimatedDragGesture.native.js.map +1 -1
  33. package/dist/cjs/useReducedMotion.cjs +47 -55
  34. package/dist/cjs/useReducedMotion.native.cjs +74 -0
  35. package/dist/cjs/useReducedMotion.native.js +53 -60
  36. package/dist/cjs/useReducedMotion.native.js.map +1 -1
  37. package/dist/cjs/useToastAnimations.cjs +198 -270
  38. package/dist/cjs/useToastAnimations.native.cjs +229 -0
  39. package/dist/cjs/useToastAnimations.native.js +205 -276
  40. package/dist/cjs/useToastAnimations.native.js.map +1 -1
  41. package/dist/esm/ToastComposable.mjs +752 -854
  42. package/dist/esm/ToastComposable.mjs.map +1 -1
  43. package/dist/esm/ToastComposable.native.js +834 -940
  44. package/dist/esm/ToastComposable.native.js.map +1 -1
  45. package/dist/esm/ToastItemFrame.mjs +31 -99
  46. package/dist/esm/ToastItemFrame.mjs.map +1 -1
  47. package/dist/esm/ToastItemFrame.native.js +34 -102
  48. package/dist/esm/ToastItemFrame.native.js.map +1 -1
  49. package/dist/esm/ToastState.mjs +266 -273
  50. package/dist/esm/ToastState.mjs.map +1 -1
  51. package/dist/esm/ToastState.native.js +265 -274
  52. package/dist/esm/ToastState.native.js.map +1 -1
  53. package/dist/esm/createNativeToast.mjs +22 -26
  54. package/dist/esm/createNativeToast.mjs.map +1 -1
  55. package/dist/esm/createNativeToast.native.js +21 -23
  56. package/dist/esm/createNativeToast.native.js.map +1 -1
  57. package/dist/esm/dispatchNativeToast.mjs +19 -17
  58. package/dist/esm/dispatchNativeToast.mjs.map +1 -1
  59. package/dist/esm/dispatchNativeToast.native.js +21 -19
  60. package/dist/esm/dispatchNativeToast.native.js.map +1 -1
  61. package/dist/esm/index.js +5 -2
  62. package/dist/esm/index.mjs +5 -2
  63. package/dist/esm/index.native.js +5 -2
  64. package/dist/esm/types.mjs +0 -2
  65. package/dist/esm/types.native.js +0 -2
  66. package/dist/esm/useAnimatedDragGesture.mjs +181 -168
  67. package/dist/esm/useAnimatedDragGesture.mjs.map +1 -1
  68. package/dist/esm/useAnimatedDragGesture.native.js +163 -166
  69. package/dist/esm/useAnimatedDragGesture.native.js.map +1 -1
  70. package/dist/esm/useReducedMotion.mjs +31 -29
  71. package/dist/esm/useReducedMotion.mjs.map +1 -1
  72. package/dist/esm/useReducedMotion.native.js +37 -35
  73. package/dist/esm/useReducedMotion.native.js.map +1 -1
  74. package/dist/esm/useToastAnimations.mjs +183 -245
  75. package/dist/esm/useToastAnimations.mjs.map +1 -1
  76. package/dist/esm/useToastAnimations.native.js +190 -252
  77. package/dist/esm/useToastAnimations.native.js.map +1 -1
  78. package/dist/jsx/ToastComposable.mjs +752 -854
  79. package/dist/jsx/ToastComposable.mjs.map +1 -1
  80. package/dist/jsx/ToastComposable.native.cjs +924 -0
  81. package/dist/jsx/ToastComposable.native.js +854 -966
  82. package/dist/jsx/ToastComposable.native.js.map +1 -1
  83. package/dist/jsx/ToastItemFrame.mjs +31 -99
  84. package/dist/jsx/ToastItemFrame.mjs.map +1 -1
  85. package/dist/jsx/ToastItemFrame.native.cjs +76 -0
  86. package/dist/jsx/ToastItemFrame.native.js +50 -120
  87. package/dist/jsx/ToastItemFrame.native.js.map +1 -1
  88. package/dist/jsx/ToastState.mjs +266 -273
  89. package/dist/jsx/ToastState.mjs.map +1 -1
  90. package/dist/jsx/ToastState.native.cjs +312 -0
  91. package/dist/jsx/ToastState.native.js +280 -290
  92. package/dist/jsx/ToastState.native.js.map +1 -1
  93. package/dist/jsx/createNativeToast.mjs +22 -26
  94. package/dist/jsx/createNativeToast.mjs.map +1 -1
  95. package/dist/jsx/createNativeToast.native.cjs +54 -0
  96. package/dist/jsx/createNativeToast.native.js +36 -40
  97. package/dist/jsx/createNativeToast.native.js.map +1 -1
  98. package/dist/jsx/dispatchNativeToast.mjs +19 -17
  99. package/dist/jsx/dispatchNativeToast.mjs.map +1 -1
  100. package/dist/jsx/dispatchNativeToast.native.cjs +48 -0
  101. package/dist/jsx/dispatchNativeToast.native.js +34 -36
  102. package/dist/jsx/dispatchNativeToast.native.js.map +1 -1
  103. package/dist/jsx/index.js +5 -2
  104. package/dist/jsx/index.mjs +5 -2
  105. package/dist/jsx/index.native.cjs +35 -0
  106. package/dist/jsx/index.native.js +26 -12
  107. package/dist/jsx/index.native.js.map +1 -1
  108. package/dist/jsx/types.mjs +0 -2
  109. package/dist/jsx/types.native.cjs +19 -0
  110. package/dist/jsx/types.native.js +10 -10
  111. package/dist/jsx/types.native.js.map +1 -1
  112. package/dist/jsx/useAnimatedDragGesture.mjs +181 -168
  113. package/dist/jsx/useAnimatedDragGesture.mjs.map +1 -1
  114. package/dist/jsx/useAnimatedDragGesture.native.cjs +210 -0
  115. package/dist/jsx/useAnimatedDragGesture.native.js +179 -191
  116. package/dist/jsx/useAnimatedDragGesture.native.js.map +1 -1
  117. package/dist/jsx/useReducedMotion.mjs +31 -29
  118. package/dist/jsx/useReducedMotion.mjs.map +1 -1
  119. package/dist/jsx/useReducedMotion.native.cjs +74 -0
  120. package/dist/jsx/useReducedMotion.native.js +53 -60
  121. package/dist/jsx/useReducedMotion.native.js.map +1 -1
  122. package/dist/jsx/useToastAnimations.mjs +183 -245
  123. package/dist/jsx/useToastAnimations.mjs.map +1 -1
  124. package/dist/jsx/useToastAnimations.native.cjs +229 -0
  125. package/dist/jsx/useToastAnimations.native.js +205 -276
  126. package/dist/jsx/useToastAnimations.native.js.map +1 -1
  127. package/package.json +20 -37
  128. package/src/ToastComposable.tsx +55 -97
  129. package/src/ToastItemFrame.tsx +22 -81
  130. package/src/ToastState.ts +1 -0
  131. package/src/index.ts +34 -2
  132. package/src/types.ts +11 -0
  133. package/src/useAnimatedDragGesture.native.ts +1 -1
  134. package/src/useAnimatedDragGesture.ts +1 -1
  135. package/src/useToastAnimations.ts +20 -36
  136. package/types/ToastComposable.d.ts +124 -47
  137. package/types/ToastComposable.d.ts.map +1 -1
  138. package/types/ToastItemFrame.d.ts +52 -21
  139. package/types/ToastItemFrame.d.ts.map +1 -1
  140. package/types/ToastState.d.ts +15 -11
  141. package/types/ToastState.d.ts.map +1 -1
  142. package/types/index.d.ts +6 -2
  143. package/types/index.d.ts.map +1 -1
  144. package/types/types.d.ts +1 -0
  145. package/types/types.d.ts.map +1 -1
  146. package/types/useAnimatedDragGesture.d.ts +1 -1
  147. package/types/useAnimatedDragGesture.d.ts.map +1 -1
  148. package/types/useAnimatedDragGesture.native.d.ts +1 -1
  149. package/types/useAnimatedDragGesture.native.d.ts.map +1 -1
  150. package/types/useToastAnimations.d.ts +2 -8
  151. package/types/useToastAnimations.d.ts.map +1 -1
  152. package/dist/cjs/Toast.cjs +0 -178
  153. package/dist/cjs/Toast.native.js +0 -181
  154. package/dist/cjs/Toast.native.js.map +0 -1
  155. package/dist/cjs/ToastAnnounce.cjs +0 -103
  156. package/dist/cjs/ToastAnnounce.native.js +0 -111
  157. package/dist/cjs/ToastAnnounce.native.js.map +0 -1
  158. package/dist/cjs/ToastImperative.cjs +0 -119
  159. package/dist/cjs/ToastImperative.native.js +0 -130
  160. package/dist/cjs/ToastImperative.native.js.map +0 -1
  161. package/dist/cjs/ToastImpl.cjs +0 -354
  162. package/dist/cjs/ToastImpl.native.js +0 -364
  163. package/dist/cjs/ToastImpl.native.js.map +0 -1
  164. package/dist/cjs/ToastPortal.cjs +0 -49
  165. package/dist/cjs/ToastPortal.native.js +0 -52
  166. package/dist/cjs/ToastPortal.native.js.map +0 -1
  167. package/dist/cjs/ToastProvider.cjs +0 -150
  168. package/dist/cjs/ToastProvider.native.js +0 -161
  169. package/dist/cjs/ToastProvider.native.js.map +0 -1
  170. package/dist/cjs/ToastViewport.cjs +0 -319
  171. package/dist/cjs/ToastViewport.native.js +0 -343
  172. package/dist/cjs/ToastViewport.native.js.map +0 -1
  173. package/dist/cjs/Toaster.cjs +0 -87
  174. package/dist/cjs/Toaster.native.js +0 -91
  175. package/dist/cjs/Toaster.native.js.map +0 -1
  176. package/dist/cjs/constants.cjs +0 -30
  177. package/dist/cjs/constants.native.js +0 -33
  178. package/dist/cjs/constants.native.js.map +0 -1
  179. package/dist/cjs/v2.cjs +0 -36
  180. package/dist/cjs/v2.native.js +0 -39
  181. package/dist/cjs/v2.native.js.map +0 -1
  182. package/dist/esm/Toast.mjs +0 -137
  183. package/dist/esm/Toast.mjs.map +0 -1
  184. package/dist/esm/Toast.native.js +0 -137
  185. package/dist/esm/Toast.native.js.map +0 -1
  186. package/dist/esm/ToastAnnounce.mjs +0 -66
  187. package/dist/esm/ToastAnnounce.mjs.map +0 -1
  188. package/dist/esm/ToastAnnounce.native.js +0 -71
  189. package/dist/esm/ToastAnnounce.native.js.map +0 -1
  190. package/dist/esm/ToastImperative.mjs +0 -80
  191. package/dist/esm/ToastImperative.mjs.map +0 -1
  192. package/dist/esm/ToastImperative.native.js +0 -88
  193. package/dist/esm/ToastImperative.native.js.map +0 -1
  194. package/dist/esm/ToastImpl.mjs +0 -316
  195. package/dist/esm/ToastImpl.mjs.map +0 -1
  196. package/dist/esm/ToastImpl.native.js +0 -329
  197. package/dist/esm/ToastImpl.native.js.map +0 -1
  198. package/dist/esm/ToastPortal.mjs +0 -24
  199. package/dist/esm/ToastPortal.mjs.map +0 -1
  200. package/dist/esm/ToastPortal.native.js +0 -24
  201. package/dist/esm/ToastPortal.native.js.map +0 -1
  202. package/dist/esm/ToastProvider.mjs +0 -110
  203. package/dist/esm/ToastProvider.mjs.map +0 -1
  204. package/dist/esm/ToastProvider.native.js +0 -118
  205. package/dist/esm/ToastProvider.native.js.map +0 -1
  206. package/dist/esm/ToastViewport.mjs +0 -280
  207. package/dist/esm/ToastViewport.mjs.map +0 -1
  208. package/dist/esm/ToastViewport.native.js +0 -301
  209. package/dist/esm/ToastViewport.native.js.map +0 -1
  210. package/dist/esm/Toaster.mjs +0 -51
  211. package/dist/esm/Toaster.mjs.map +0 -1
  212. package/dist/esm/Toaster.native.js +0 -52
  213. package/dist/esm/Toaster.native.js.map +0 -1
  214. package/dist/esm/constants.mjs +0 -4
  215. package/dist/esm/constants.mjs.map +0 -1
  216. package/dist/esm/constants.native.js +0 -4
  217. package/dist/esm/constants.native.js.map +0 -1
  218. package/dist/esm/index.js.map +0 -1
  219. package/dist/esm/index.mjs.map +0 -1
  220. package/dist/esm/index.native.js.map +0 -1
  221. package/dist/esm/types.mjs.map +0 -1
  222. package/dist/esm/types.native.js.map +0 -1
  223. package/dist/esm/v2.mjs +0 -6
  224. package/dist/esm/v2.mjs.map +0 -1
  225. package/dist/esm/v2.native.js +0 -6
  226. package/dist/esm/v2.native.js.map +0 -1
  227. package/dist/jsx/Toast.mjs +0 -137
  228. package/dist/jsx/Toast.mjs.map +0 -1
  229. package/dist/jsx/Toast.native.js +0 -181
  230. package/dist/jsx/Toast.native.js.map +0 -1
  231. package/dist/jsx/ToastAnnounce.mjs +0 -66
  232. package/dist/jsx/ToastAnnounce.mjs.map +0 -1
  233. package/dist/jsx/ToastAnnounce.native.js +0 -111
  234. package/dist/jsx/ToastAnnounce.native.js.map +0 -1
  235. package/dist/jsx/ToastImperative.mjs +0 -80
  236. package/dist/jsx/ToastImperative.mjs.map +0 -1
  237. package/dist/jsx/ToastImperative.native.js +0 -130
  238. package/dist/jsx/ToastImperative.native.js.map +0 -1
  239. package/dist/jsx/ToastImpl.mjs +0 -316
  240. package/dist/jsx/ToastImpl.mjs.map +0 -1
  241. package/dist/jsx/ToastImpl.native.js +0 -364
  242. package/dist/jsx/ToastImpl.native.js.map +0 -1
  243. package/dist/jsx/ToastPortal.mjs +0 -24
  244. package/dist/jsx/ToastPortal.mjs.map +0 -1
  245. package/dist/jsx/ToastPortal.native.js +0 -52
  246. package/dist/jsx/ToastPortal.native.js.map +0 -1
  247. package/dist/jsx/ToastProvider.mjs +0 -110
  248. package/dist/jsx/ToastProvider.mjs.map +0 -1
  249. package/dist/jsx/ToastProvider.native.js +0 -161
  250. package/dist/jsx/ToastProvider.native.js.map +0 -1
  251. package/dist/jsx/ToastViewport.mjs +0 -280
  252. package/dist/jsx/ToastViewport.mjs.map +0 -1
  253. package/dist/jsx/ToastViewport.native.js +0 -343
  254. package/dist/jsx/ToastViewport.native.js.map +0 -1
  255. package/dist/jsx/Toaster.mjs +0 -51
  256. package/dist/jsx/Toaster.mjs.map +0 -1
  257. package/dist/jsx/Toaster.native.js +0 -91
  258. package/dist/jsx/Toaster.native.js.map +0 -1
  259. package/dist/jsx/constants.mjs +0 -4
  260. package/dist/jsx/constants.mjs.map +0 -1
  261. package/dist/jsx/constants.native.js +0 -33
  262. package/dist/jsx/constants.native.js.map +0 -1
  263. package/dist/jsx/index.js.map +0 -1
  264. package/dist/jsx/index.mjs.map +0 -1
  265. package/dist/jsx/types.mjs.map +0 -1
  266. package/dist/jsx/v2.mjs +0 -6
  267. package/dist/jsx/v2.mjs.map +0 -1
  268. package/dist/jsx/v2.native.js +0 -39
  269. package/dist/jsx/v2.native.js.map +0 -1
  270. package/src/Toast.tsx +0 -219
  271. package/src/ToastAnnounce.tsx +0 -103
  272. package/src/ToastImperative.tsx +0 -192
  273. package/src/ToastImpl.tsx +0 -514
  274. package/src/ToastPortal.tsx +0 -19
  275. package/src/ToastProvider.tsx +0 -204
  276. package/src/ToastViewport.tsx +0 -430
  277. package/src/Toaster.tsx +0 -181
  278. package/src/constants.ts +0 -2
  279. package/src/v2.ts +0 -31
  280. package/types/Toast.d.ts +0 -84
  281. package/types/Toast.d.ts.map +0 -1
  282. package/types/ToastAnnounce.d.ts +0 -18
  283. package/types/ToastAnnounce.d.ts.map +0 -1
  284. package/types/ToastImperative.d.ts +0 -95
  285. package/types/ToastImperative.d.ts.map +0 -1
  286. package/types/ToastImpl.d.ts +0 -109
  287. package/types/ToastImpl.d.ts.map +0 -1
  288. package/types/ToastPortal.d.ts +0 -8
  289. package/types/ToastPortal.d.ts.map +0 -1
  290. package/types/ToastProvider.d.ts +0 -92
  291. package/types/ToastProvider.d.ts.map +0 -1
  292. package/types/ToastViewport.d.ts +0 -75
  293. package/types/ToastViewport.d.ts.map +0 -1
  294. package/types/Toaster.d.ts +0 -112
  295. package/types/Toaster.d.ts.map +0 -1
  296. package/types/constants.d.ts +0 -3
  297. package/types/constants.d.ts.map +0 -1
  298. package/types/v2.d.ts +0 -10
  299. package/types/v2.d.ts.map +0 -1
  300. package/v2/index.cjs +0 -2
  301. package/v2/index.js +0 -2
  302. package/v2/index.native.cjs +0 -2
  303. package/v2/index.native.js +0 -2
@@ -1,204 +0,0 @@
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 =
21
- | 'vertical'
22
- | 'up'
23
- | 'down'
24
- | 'horizontal'
25
- | 'left'
26
- | 'right'
27
- | 'auto'
28
-
29
- export type ToastProviderContextValue = {
30
- id: string
31
- toastScope: string
32
- label: string
33
- duration: number
34
- swipeDirection: SwipeDirection
35
- swipeThreshold: number
36
- toastCount: number
37
- viewports: Record<string, TamaguiElement | null>
38
- onViewportChange(name: string, viewport: TamaguiElement): void
39
- onToastAdd(): void
40
- onToastRemove(): void
41
- isFocusedToastEscapeKeyDownRef: React.MutableRefObject<boolean>
42
- isClosePausedRef: React.MutableRefObject<boolean>
43
- options: ToastImperativeOptions
44
- }
45
-
46
- export type ToastScopes = string
47
-
48
- type ScopedProps<P> = Omit<P, 'scope'> & { scope?: ToastScopes }
49
-
50
- const { Provider: ToastProviderProvider, useStyledContext: useToastProviderContext } =
51
- createStyledContext<ToastProviderContextValue>(
52
- // since we always provide this we can avoid setting here
53
- {} as ToastProviderContextValue,
54
- 'Toast__'
55
- )
56
-
57
- interface ToastProviderProps {
58
- children?: React.ReactNode
59
- /**
60
- * An author-localized label for each toast. Used to help screen reader users
61
- * associate the interruption with a toast.
62
- * @defaultValue 'Notification'
63
- */
64
- label?: string
65
- /**
66
- * Time in milliseconds that each toast should remain visible for.
67
- * @defaultValue 5000
68
- */
69
- duration?: number
70
- /**
71
- * Direction of pointer swipe that should close the toast.
72
- * @defaultValue 'right'
73
- */
74
- swipeDirection?: SwipeDirection
75
- /**
76
- * Distance in pixels that the swipe must pass before a close is triggered.
77
- * @defaultValue 50
78
- */
79
- swipeThreshold?: number
80
- /**
81
- * @defaultValue unique generated identifier
82
- */
83
- id?: string
84
- /**
85
- * 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`.
86
- * Only works with the imperative `useToast` hook.
87
- */
88
- native?: NativeValue
89
- /**
90
- * Options for the burnt package if you're using native toasts on mobile
91
- */
92
- burntOptions?: Omit<BurntToastOptions, 'title' | 'message' | 'duration'>
93
- /**
94
- * Options for the notification API if you're using native toasts on web
95
- */
96
- notificationOptions?: NotificationOptions
97
- }
98
-
99
- const ToastProvider: React.FC<ToastProviderProps> = (
100
- props: ScopedProps<ToastProviderProps>
101
- ) => {
102
- const {
103
- scope = TOAST_CONTEXT,
104
- id: providedId,
105
- burntOptions,
106
- native,
107
- notificationOptions,
108
- label = 'Notification',
109
- duration = 5000,
110
- swipeDirection = 'right',
111
- swipeThreshold = 50,
112
- children,
113
- } = props
114
- const backupId = React.useId()
115
- const id = providedId ?? backupId
116
- const [viewports, setViewports] = React.useState<
117
- ToastProviderContextValue['viewports']
118
- >({})
119
- const [toastCount, setToastCount] = React.useState(0)
120
- const isFocusedToastEscapeKeyDownRef = React.useRef(false)
121
- const isClosePausedRef = React.useRef(false)
122
-
123
- const handleViewportChange = React.useCallback(
124
- (name: string, viewport: TamaguiElement | null) => {
125
- startTransition(() => {
126
- setViewports((prev) => ({ ...prev, [name]: viewport }))
127
- })
128
- },
129
- []
130
- )
131
-
132
- // memo context to avoid expensive re-renders
133
- const options = React.useMemo(() => {
134
- return {
135
- duration,
136
- burntOptions,
137
- native,
138
- notificationOptions,
139
- }
140
- // nested simple object so JSON.stringify
141
- }, [JSON.stringify([duration, burntOptions, native, notificationOptions])])
142
-
143
- return (
144
- <Collection.Provider scope={scope}>
145
- <ToastProviderProvider
146
- scope={scope}
147
- id={id}
148
- label={label}
149
- duration={duration}
150
- swipeDirection={swipeDirection}
151
- swipeThreshold={swipeThreshold}
152
- toastCount={toastCount}
153
- viewports={viewports}
154
- onViewportChange={handleViewportChange}
155
- onToastAdd={React.useCallback(() => {
156
- startTransition(() => {
157
- setToastCount((prevCount) => prevCount + 1)
158
- })
159
- }, [])}
160
- onToastRemove={React.useCallback(() => {
161
- startTransition(() => {
162
- setToastCount((prevCount) => prevCount - 1)
163
- })
164
- }, [])}
165
- isFocusedToastEscapeKeyDownRef={isFocusedToastEscapeKeyDownRef}
166
- isClosePausedRef={isClosePausedRef}
167
- options={options}
168
- >
169
- <ToastImperativeProvider options={options}>{children}</ToastImperativeProvider>
170
- </ToastProviderProvider>
171
- </Collection.Provider>
172
- )
173
- }
174
-
175
- export function ReprogapateToastProvider(props: {
176
- children: React.ReactNode
177
- context: ToastProviderContextValue
178
- }) {
179
- const { children, context } = props
180
- return (
181
- <Collection.Provider scope={context.toastScope}>
182
- <ToastProviderProvider {...context}>
183
- <ToastImperativeProvider options={context.options}>
184
- {children}
185
- </ToastImperativeProvider>
186
- </ToastProviderProvider>
187
- </Collection.Provider>
188
- )
189
- }
190
-
191
- ToastProvider.propTypes = {
192
- label(props) {
193
- if (props.label && typeof props.label === 'string' && !props.label.trim()) {
194
- const error = `Invalid prop \`label\` supplied to \`${PROVIDER_NAME}\`. Expected non-empty \`string\`.`
195
- return new Error(error)
196
- }
197
- return null
198
- },
199
- }
200
-
201
- ToastProvider.displayName = PROVIDER_NAME
202
-
203
- export { Collection, ToastProvider, useCollection, useToastProviderContext }
204
- export type { ScopedProps, ToastProviderProps }
@@ -1,430 +0,0 @@
1
- import { AnimatePresence } from '@tamagui/animate-presence'
2
- import { useComposedRefs } from '@tamagui/compose-refs'
3
- import { isWeb } from '@tamagui/constants'
4
- import type { GetProps, TamaguiElement } from '@tamagui/core'
5
- import { styled } from '@tamagui/core'
6
- import { PortalHost } from '@tamagui/portal'
7
- import { YStack } from '@tamagui/stacks'
8
- import { VisuallyHidden } from '@tamagui/visually-hidden'
9
- import * as React from 'react'
10
- import { TOAST_CONTEXT } from './constants'
11
- import { ToastPortal } from './ToastPortal'
12
- import type { ScopedProps, ToastProviderContextValue } from './ToastProvider'
13
- import { Collection, useCollection, useToastProviderContext } from './ToastProvider'
14
-
15
- const VIEWPORT_NAME = 'ToastViewport'
16
- const VIEWPORT_DEFAULT_HOTKEY = ['F8']
17
- const VIEWPORT_PAUSE = 'toast.viewportPause'
18
- const VIEWPORT_RESUME = 'toast.viewportResume'
19
-
20
- const ToastViewportWrapperFrame = styled(YStack, {
21
- name: 'ViewportWrapper',
22
-
23
- variants: {
24
- unstyled: {
25
- false: {
26
- pointerEvents: 'box-none',
27
- top: 0,
28
- bottom: 0,
29
- left: 0,
30
- right: 0,
31
- position: isWeb ? ('fixed' as any) : 'absolute',
32
- maxWidth: '100%',
33
- tabIndex: 0,
34
- zIndex: 100000,
35
- },
36
- },
37
- } as const,
38
-
39
- defaultVariants: {
40
- unstyled: process.env.TAMAGUI_HEADLESS === '1',
41
- },
42
- })
43
-
44
- const ToastViewportFrame = styled(YStack, {
45
- name: VIEWPORT_NAME,
46
-
47
- variants: {
48
- unstyled: {
49
- false: {
50
- pointerEvents: 'box-none',
51
- position: isWeb ? ('fixed' as any) : 'absolute',
52
- maxWidth: '100%',
53
- },
54
- },
55
- } as const,
56
-
57
- defaultVariants: {
58
- unstyled: process.env.TAMAGUI_HEADLESS === '1',
59
- },
60
- })
61
-
62
- type ToastViewportFrameProps = GetProps<typeof ToastViewportFrame>
63
- type ToastViewportProps = ToastViewportFrameProps & {
64
- /**
65
- * The keys to use as the keyboard shortcut that will move focus to the toast viewport.
66
- * @defaultValue ['F8']
67
- */
68
- hotkey?: string[]
69
- /**
70
- * An author-localized label for the toast viewport to provide context for screen reader users
71
- * when navigating page landmarks. The available `{hotkey}` placeholder will be replaced for you.
72
- * @defaultValue 'Notifications ({hotkey})'
73
- */
74
- label?: string
75
- /**
76
- * Used to reference the viewport if you want to have multiple viewports in the same provider.
77
- */
78
- name?: string
79
- /**
80
- * Pass this when you want to have multiple/duplicated toasts.
81
- */
82
- multipleToasts?: boolean
83
- /**
84
- * When true, uses a portal to render at the very top of the root TamaguiProvider.
85
- */
86
- portalToRoot?: boolean
87
- }
88
-
89
- const ToastViewport = React.memo(
90
- React.forwardRef<HTMLDivElement, ToastViewportProps>(
91
- (props: ScopedProps<ToastViewportProps>, forwardedRef) => {
92
- const {
93
- scope,
94
- hotkey = VIEWPORT_DEFAULT_HOTKEY,
95
- label = 'Notifications ({hotkey})',
96
- name = 'default',
97
- multipleToasts,
98
- zIndex,
99
- portalToRoot,
100
- ...viewportProps
101
- } = props
102
- const context = useToastProviderContext(scope)
103
- const getItems = useCollection(scope || TOAST_CONTEXT)
104
- const headFocusProxyRef = React.useRef<FocusProxyElement>(null)
105
- const tailFocusProxyRef = React.useRef<FocusProxyElement>(null)
106
- const wrapperRef = React.useRef<HTMLDivElement>(null)
107
- const ref = React.useRef<HTMLDivElement>(null)
108
- const onViewportChange = React.useCallback(
109
- (el: TamaguiElement) => {
110
- if (context.viewports[name] !== el) context.onViewportChange(name, el)
111
- },
112
- [name, context.viewports]
113
- )
114
- // @ts-ignore TODO react 19 type needs fix
115
- const composedRefs = useComposedRefs(forwardedRef, ref, onViewportChange)
116
- const hotkeyLabel = hotkey.join('+').replace(/Key/g, '').replace(/Digit/g, '')
117
- const hasToasts = context.toastCount > 0
118
-
119
- React.useEffect(() => {
120
- if (!isWeb) return
121
- if (context.toastCount === 0) return
122
- const handleKeyDown = (event: KeyboardEvent) => {
123
- // we use `event.code` as it is consistent regardless of meta keys that were pressed.
124
- // for example, `event.key` for `Control+Alt+t` is `†` and `t !== †`
125
- const isHotkeyPressed = hotkey.every(
126
- (key) => (event as any)[key] || event.code === key
127
- )
128
- if (isHotkeyPressed) ref.current?.focus()
129
- }
130
- document.addEventListener('keydown', handleKeyDown)
131
- return () => {
132
- document.removeEventListener('keydown', handleKeyDown)
133
- }
134
- }, [hotkey, context.toastCount])
135
-
136
- React.useEffect(() => {
137
- if (!isWeb) return
138
- if (context.toastCount === 0) return
139
- const wrapper = wrapperRef.current
140
- const viewport = ref.current
141
- if (hasToasts && wrapper && viewport) {
142
- const handlePause = () => {
143
- if (!context.isClosePausedRef.current) {
144
- const pauseEvent = new CustomEvent(VIEWPORT_PAUSE)
145
- viewport.dispatchEvent(pauseEvent)
146
- context.isClosePausedRef.current = true
147
- }
148
- }
149
-
150
- const handleResume = () => {
151
- if (context.isClosePausedRef.current) {
152
- const resumeEvent = new CustomEvent(VIEWPORT_RESUME)
153
- viewport.dispatchEvent(resumeEvent)
154
- context.isClosePausedRef.current = false
155
- }
156
- }
157
-
158
- const handleFocusOutResume = (event: FocusEvent) => {
159
- const isFocusMovingOutside = !wrapper.contains(
160
- event.relatedTarget as HTMLElement
161
- )
162
- if (isFocusMovingOutside) handleResume()
163
- }
164
-
165
- const handlePointerLeaveResume = () => {
166
- const isFocusInside = wrapper.contains(document.activeElement)
167
- if (!isFocusInside) handleResume()
168
- }
169
-
170
- // Toasts are not in the viewport React tree so we need to bind DOM events
171
- wrapper.addEventListener('focusin', handlePause)
172
- wrapper.addEventListener('focusout', handleFocusOutResume)
173
- wrapper.addEventListener('pointermove', handlePause)
174
- wrapper.addEventListener('pointerleave', handlePointerLeaveResume)
175
- window.addEventListener('blur', handlePause)
176
- window.addEventListener('focus', handleResume)
177
- return () => {
178
- wrapper.removeEventListener('focusin', handlePause)
179
- wrapper.removeEventListener('focusout', handleFocusOutResume)
180
- wrapper.removeEventListener('pointermove', handlePause)
181
- wrapper.removeEventListener('pointerleave', handlePointerLeaveResume)
182
- window.removeEventListener('blur', handlePause)
183
- window.removeEventListener('focus', handleResume)
184
- }
185
- }
186
- }, [hasToasts, context.isClosePausedRef, context.toastCount])
187
-
188
- const getSortedTabbableCandidates = React.useCallback(
189
- ({ tabbingDirection }: { tabbingDirection: 'forwards' | 'backwards' }) => {
190
- const toastItems = getItems()
191
- const tabbableCandidates = toastItems.map((toastItem) => {
192
- const toastNode = toastItem.ref.current!
193
- const toastTabbableCandidates = [
194
- toastNode,
195
- ...getTabbableCandidates(toastNode),
196
- ]
197
- return tabbingDirection === 'forwards'
198
- ? toastTabbableCandidates
199
- : toastTabbableCandidates.reverse()
200
- })
201
- return (
202
- tabbingDirection === 'forwards'
203
- ? tabbableCandidates.reverse()
204
- : tabbableCandidates
205
- ).flat()
206
- },
207
- [getItems]
208
- )
209
-
210
- React.useEffect(() => {
211
- if (!isWeb) return
212
- if (context.toastCount === 0) return
213
-
214
- const viewport = ref.current
215
- // We programmatically manage tabbing as we are unable to influence
216
- // the source order with portals, this allows us to reverse the
217
- // tab order so that it runs from most recent toast to least
218
- if (viewport) {
219
- const handleKeyDown = (event: KeyboardEvent) => {
220
- const isMetaKey = event.altKey || event.ctrlKey || event.metaKey
221
- const isTabKey = event.key === 'Tab' && !isMetaKey
222
-
223
- if (isTabKey) {
224
- const focusedElement = document.activeElement
225
- const isTabbingBackwards = event.shiftKey
226
- const targetIsViewport = event.target === viewport
227
-
228
- // If we're back tabbing after jumping to the viewport then we simply
229
- // proxy focus out to the preceding document
230
- if (targetIsViewport && isTabbingBackwards) {
231
- // @ts-ignore ali TODO type
232
- headFocusProxyRef.current?.focus()
233
- return
234
- }
235
-
236
- const tabbingDirection = isTabbingBackwards ? 'backwards' : 'forwards'
237
- const sortedCandidates = getSortedTabbableCandidates({ tabbingDirection })
238
- const index = sortedCandidates.findIndex(
239
- (candidate) => candidate === focusedElement
240
- )
241
- if (focusFirst(sortedCandidates.slice(index + 1) as any)) {
242
- event.preventDefault()
243
- } else {
244
- // If we can't focus that means we're at the edges so we
245
- // proxy to the corresponding exit point and let the browser handle
246
- // tab/shift+tab keypress and implicitly pass focus to the next valid element in the document
247
- isTabbingBackwards
248
- ? // @ts-ignore ali TODO type
249
- headFocusProxyRef.current?.focus()
250
- : // @ts-ignore ali TODO type
251
- tailFocusProxyRef.current?.focus()
252
- }
253
- }
254
- }
255
-
256
- // Toasts are not in the viewport React tree so we need to bind DOM events
257
- viewport.addEventListener('keydown', handleKeyDown)
258
- return () => viewport.removeEventListener('keydown', handleKeyDown)
259
- }
260
- }, [getItems, getSortedTabbableCandidates, context.toastCount])
261
-
262
- const contents = (
263
- <ToastViewportWrapperFrame
264
- ref={wrapperRef as any}
265
- // biome-ignore lint/a11y/useSemanticElements: <explanation>
266
- role="region"
267
- aria-label={label.replace('{hotkey}', hotkeyLabel)}
268
- // // Ensure virtual cursor from landmarks menus triggers focus/blur for pause/resume
269
- tabIndex={-1}
270
- // // incase list has size when empty (e.g. padding), we remove pointer events so
271
- // // it doesn't prevent interactions with page elements that it overlays
272
- // pointerEvents={hasToasts ? undefined : 'none'}
273
- >
274
- {hasToasts && (
275
- <FocusProxy
276
- context={context}
277
- viewportName={name}
278
- ref={headFocusProxyRef}
279
- onFocusFromOutsideViewport={() => {
280
- const tabbableCandidates = getSortedTabbableCandidates({
281
- tabbingDirection: 'forwards',
282
- })
283
- focusFirst(tabbableCandidates as any)
284
- }}
285
- />
286
- )}
287
- {/**
288
- * tabindex on the the list so that it can be focused when items are removed. we focus
289
- * the list instead of the viewport so it announces number of items remaining.
290
- */}
291
- <Collection.Slot scope={context.toastScope}>
292
- <ToastViewportFrame
293
- focusable={context.toastCount > 0}
294
- ref={composedRefs as any}
295
- {...viewportProps}
296
- >
297
- <PortalHost
298
- render={(children) => (
299
- <AnimatePresence exitBeforeEnter={!multipleToasts}>
300
- {children}
301
- </AnimatePresence>
302
- )}
303
- name={name ?? 'default'}
304
- />
305
- </ToastViewportFrame>
306
- </Collection.Slot>
307
- {hasToasts && (
308
- <FocusProxy
309
- context={context}
310
- viewportName={name}
311
- ref={tailFocusProxyRef}
312
- onFocusFromOutsideViewport={() => {
313
- const tabbableCandidates = getSortedTabbableCandidates({
314
- tabbingDirection: 'backwards',
315
- })
316
- focusFirst(tabbableCandidates as any)
317
- }}
318
- />
319
- )}
320
- </ToastViewportWrapperFrame>
321
- )
322
-
323
- if (portalToRoot) {
324
- return (
325
- <ToastPortal
326
- context={context}
327
- {...(typeof zIndex === 'number' ? { zIndex } : {})}
328
- >
329
- {contents}
330
- </ToastPortal>
331
- )
332
- }
333
-
334
- return contents
335
- }
336
- )
337
- )
338
-
339
- ToastViewport.displayName = VIEWPORT_NAME
340
-
341
- /* -----------------------------------------------------------------------------------------------*/
342
-
343
- const FOCUS_PROXY_NAME = 'ToastFocusProxy'
344
-
345
- type FocusProxyElement = React.ElementRef<typeof VisuallyHidden>
346
-
347
- type VisuallyHiddenProps = GetProps<typeof VisuallyHidden>
348
-
349
- interface FocusProxyProps extends VisuallyHiddenProps {
350
- onFocusFromOutsideViewport(): void
351
- viewportName: string
352
- context: ToastProviderContextValue
353
- }
354
-
355
- const FocusProxy = React.forwardRef<FocusProxyElement, ScopedProps<FocusProxyProps>>(
356
- (props, forwardedRef) => {
357
- const { onFocusFromOutsideViewport, viewportName, context, ...proxyProps } = props
358
- const viewport = context.viewports[viewportName] as HTMLElement
359
-
360
- return (
361
- <VisuallyHidden
362
- aria-hidden
363
- tabIndex={0}
364
- {...proxyProps}
365
- ref={forwardedRef}
366
- // Avoid page scrolling when focus is on the focus proxy
367
- position={isWeb ? ('fixed' as any) : 'absolute'}
368
- onFocus={(event) => {
369
- if (!isWeb) return
370
- const prevFocusedElement = event.relatedTarget as HTMLElement | null
371
- const isFocusFromOutsideViewport = !viewport?.contains(prevFocusedElement)
372
- if (isFocusFromOutsideViewport) onFocusFromOutsideViewport()
373
- }}
374
- />
375
- )
376
- }
377
- )
378
-
379
- FocusProxy.displayName = FOCUS_PROXY_NAME
380
-
381
- /* -----------------------------------------------------------------------------------------------*/
382
-
383
- function focusFirst(candidates: TamaguiElement[]) {
384
- if (!isWeb) return
385
- const previouslyFocusedElement = document.activeElement
386
- return candidates.some((candidate) => {
387
- // if focus is already where we want to go, we don't want to keep going through the candidates
388
- if (candidate === previouslyFocusedElement) return true
389
- candidate.focus()
390
- return document.activeElement !== previouslyFocusedElement
391
- })
392
- }
393
-
394
- /**
395
- * Returns a list of potential tabbable candidates.
396
- *
397
- * NOTE: This is only a close approximation. For example it doesn't take into account cases like when
398
- * elements are not visible. This cannot be worked out easily by just reading a property, but rather
399
- * necessitate runtime knowledge (computed styles, etc). We deal with these cases separately.
400
- *
401
- * See: https://developer.mozilla.org/en-US/docs/Web/API/TreeWalker
402
- * Credit: https://github.com/discord/focus-layers/blob/master/src/util/wrapFocus.tsx#L1
403
- */
404
- function getTabbableCandidates(container: TamaguiElement) {
405
- if (!isWeb) return []
406
- const containerHtml = container as HTMLElement
407
- const nodes: HTMLElement[] = []
408
- const walker = document.createTreeWalker(containerHtml, NodeFilter.SHOW_ELEMENT, {
409
- acceptNode: (node: any) => {
410
- const isHiddenInput = node.tagName === 'INPUT' && node.type === 'hidden'
411
- if (node.disabled || node.hidden || isHiddenInput) return NodeFilter.FILTER_SKIP
412
- // `.tabIndex` is not the same as the `tabindex` attribute. It works on the
413
- // runtime's understanding of tabbability, so this automatically accounts
414
- // for any kind of element that could be tabbed to.
415
- return node.tabIndex >= 0 ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP
416
- },
417
- })
418
- while (walker.nextNode()) nodes.push(walker.currentNode as HTMLElement)
419
- // we do not take into account the order of nodes with positive `tabIndex` as it
420
- // hinders accessibility to have tab order different from visual order.
421
- return nodes
422
- }
423
-
424
- export {
425
- ToastViewport,
426
- VIEWPORT_DEFAULT_HOTKEY,
427
- VIEWPORT_PAUSE,
428
- VIEWPORT_RESUME,
429
- type ToastViewportProps,
430
- }