@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,445 @@
1
+ import { AnimatePresence } from '@tamagui/animate-presence'
2
+ import { isWeb } from '@tamagui/constants'
3
+ import type { TamaguiElement } from '@tamagui/core'
4
+ import { Theme, View, styled, useThemeName } from '@tamagui/core'
5
+ import { Portal } from '@tamagui/portal'
6
+ import * as React from 'react'
7
+ import { ToastItem } from './ToastItem'
8
+ import type { SwipeDirection } from './ToastProvider'
9
+ import type { ExternalToast, ToastT, ToastToDismiss } from './ToastState'
10
+ import { ToastState } from './ToastState'
11
+ import type { BurntToastOptions } from './types'
12
+
13
+ // defaults
14
+ const VISIBLE_TOASTS_AMOUNT = 4
15
+ const VIEWPORT_OFFSET = 24
16
+ const TOAST_GAP = 14
17
+ const TOAST_LIFETIME = 4000
18
+
19
+ export type ToasterPosition =
20
+ | 'top-left'
21
+ | 'top-center'
22
+ | 'top-right'
23
+ | 'bottom-left'
24
+ | 'bottom-center'
25
+ | 'bottom-right'
26
+
27
+ export interface HeightT {
28
+ toastId: string | number
29
+ height: number
30
+ position?: ToasterPosition
31
+ }
32
+
33
+ const TOAST_WIDTH = 356
34
+
35
+ const ToasterFrame = styled(View, {
36
+ name: 'Toaster',
37
+
38
+ variants: {
39
+ unstyled: {
40
+ false: {
41
+ position: 'fixed',
42
+ zIndex: 100000,
43
+ pointerEvents: 'box-none',
44
+ maxWidth: '100%',
45
+ // need min-height to contain absolutely positioned toasts
46
+ // toasts will overflow upward/downward from their anchor position
47
+ minHeight: 1,
48
+ },
49
+ },
50
+ } as const,
51
+
52
+ defaultVariants: {
53
+ unstyled: process.env.TAMAGUI_HEADLESS === '1',
54
+ },
55
+ })
56
+
57
+ export interface ToasterProps {
58
+ /**
59
+ * Position of the toasts on screen
60
+ * @default 'bottom-right'
61
+ */
62
+ position?: ToasterPosition
63
+
64
+ /**
65
+ * Width of toast container in pixels
66
+ * @default 356
67
+ */
68
+ width?: number
69
+
70
+ /**
71
+ * Expand toasts on hover to show all
72
+ * @default false
73
+ */
74
+ expand?: boolean
75
+
76
+ /**
77
+ * Number of toasts visible at once
78
+ * @default 4
79
+ */
80
+ visibleToasts?: number
81
+
82
+ /**
83
+ * Gap between toasts in pixels
84
+ * @default 14
85
+ */
86
+ gap?: number
87
+
88
+ /**
89
+ * Default duration for toasts in ms
90
+ * @default 4000
91
+ */
92
+ duration?: number
93
+
94
+ /**
95
+ * Offset from screen edge in pixels
96
+ * @default 24
97
+ */
98
+ offset?: number | { top?: number; right?: number; bottom?: number; left?: number }
99
+
100
+ /**
101
+ * Hotkey to focus toast viewport
102
+ * @default ['altKey', 'KeyT']
103
+ */
104
+ hotkey?: string[]
105
+
106
+ /**
107
+ * Direction(s) toasts can be swiped to dismiss
108
+ * @default 'right'
109
+ */
110
+ swipeDirection?: SwipeDirection
111
+
112
+ /**
113
+ * Distance in pixels swipe must pass to dismiss
114
+ * @default 50
115
+ */
116
+ swipeThreshold?: number
117
+
118
+ /**
119
+ * Show close button on toasts
120
+ * @default false
121
+ */
122
+ closeButton?: boolean
123
+
124
+ /**
125
+ * Theme for toasts (auto-detected if not set)
126
+ */
127
+ theme?: 'light' | 'dark' | 'system'
128
+
129
+ /**
130
+ * Custom icons for toast types
131
+ */
132
+ icons?: {
133
+ success?: React.ReactNode
134
+ error?: React.ReactNode
135
+ warning?: React.ReactNode
136
+ info?: React.ReactNode
137
+ loading?: React.ReactNode
138
+ close?: React.ReactNode
139
+ }
140
+
141
+ /**
142
+ * Default toast options
143
+ */
144
+ toastOptions?: ExternalToast
145
+
146
+ /**
147
+ * Container aria label for screen readers
148
+ * @default 'Notifications'
149
+ */
150
+ containerAriaLabel?: string
151
+
152
+ /**
153
+ * Disable native toast on mobile (uses burnt package)
154
+ * @default false
155
+ */
156
+ disableNative?: boolean
157
+
158
+ /**
159
+ * Options for burnt native toasts on mobile
160
+ */
161
+ burntOptions?: Omit<BurntToastOptions, 'title' | 'message' | 'duration'>
162
+
163
+ /**
164
+ * Options for web Notification API
165
+ */
166
+ notificationOptions?: NotificationOptions
167
+
168
+ /**
169
+ * Custom className for the container
170
+ */
171
+ className?: string
172
+
173
+ /**
174
+ * Custom style for the container
175
+ */
176
+ style?: React.CSSProperties
177
+ }
178
+
179
+ export const Toaster = React.forwardRef<TamaguiElement, ToasterProps>(
180
+ function Toaster(props, _ref) {
181
+ const {
182
+ position = 'bottom-right',
183
+ width = TOAST_WIDTH,
184
+ expand = false,
185
+ visibleToasts = VISIBLE_TOASTS_AMOUNT,
186
+ gap = TOAST_GAP,
187
+ duration = TOAST_LIFETIME,
188
+ offset = VIEWPORT_OFFSET,
189
+ hotkey = ['altKey', 'KeyT'],
190
+ swipeDirection = 'right',
191
+ swipeThreshold = 50,
192
+ closeButton = false,
193
+ theme: themeProp,
194
+ icons,
195
+ toastOptions,
196
+ containerAriaLabel = 'Notifications',
197
+ disableNative = false,
198
+ burntOptions,
199
+ notificationOptions,
200
+ className,
201
+ style,
202
+ } = props
203
+
204
+ const [toasts, setToasts] = React.useState<ToastT[]>([])
205
+ const [heights, setHeights] = React.useState<HeightT[]>([])
206
+ const [expanded, setExpanded] = React.useState(false)
207
+ const [interacting, setInteracting] = React.useState(false)
208
+
209
+ const listRef = React.useRef<TamaguiElement>(null)
210
+ const lastFocusedElementRef = React.useRef<HTMLElement | null>(null)
211
+ const isFocusWithinRef = React.useRef(false)
212
+
213
+ // subscribe to toast state changes
214
+ React.useEffect(() => {
215
+ return ToastState.subscribe((toast) => {
216
+ if ((toast as ToastToDismiss).dismiss) {
217
+ // mark toast for deletion animation
218
+ setToasts((toasts) =>
219
+ toasts.map((t) => (t.id === toast.id ? { ...t, delete: true } : t))
220
+ )
221
+ return
222
+ }
223
+
224
+ // add or update toast
225
+ setToasts((toasts) => {
226
+ const indexOfExistingToast = toasts.findIndex((t) => t.id === toast.id)
227
+
228
+ if (indexOfExistingToast !== -1) {
229
+ // update existing
230
+ return [
231
+ ...toasts.slice(0, indexOfExistingToast),
232
+ { ...toasts[indexOfExistingToast], ...toast },
233
+ ...toasts.slice(indexOfExistingToast + 1),
234
+ ]
235
+ }
236
+
237
+ // add new toast at the beginning
238
+ return [toast as ToastT, ...toasts]
239
+ })
240
+ })
241
+ }, [])
242
+
243
+ // collapse expanded view when only 1 toast remains
244
+ React.useEffect(() => {
245
+ if (toasts.length <= 1) {
246
+ setExpanded(false)
247
+ }
248
+ }, [toasts.length])
249
+
250
+ // keyboard hotkey handler
251
+ React.useEffect(() => {
252
+ if (!isWeb) return
253
+
254
+ const handleKeyDown = (event: KeyboardEvent) => {
255
+ const isHotkeyPressed =
256
+ hotkey.length > 0 &&
257
+ hotkey.every((key) => (event as any)[key] || event.code === key)
258
+
259
+ if (isHotkeyPressed) {
260
+ setExpanded(true)
261
+ ;(listRef.current as HTMLElement)?.focus()
262
+ }
263
+
264
+ if (
265
+ event.code === 'Escape' &&
266
+ (document.activeElement === listRef.current ||
267
+ (listRef.current as HTMLElement)?.contains(document.activeElement))
268
+ ) {
269
+ setExpanded(false)
270
+ }
271
+ }
272
+
273
+ document.addEventListener('keydown', handleKeyDown)
274
+ return () => document.removeEventListener('keydown', handleKeyDown)
275
+ }, [hotkey])
276
+
277
+ // restore focus when toaster unmounts
278
+ React.useEffect(() => {
279
+ if (!isWeb || !listRef.current) return
280
+
281
+ return () => {
282
+ if (lastFocusedElementRef.current) {
283
+ lastFocusedElementRef.current.focus({ preventScroll: true })
284
+ lastFocusedElementRef.current = null
285
+ isFocusWithinRef.current = false
286
+ }
287
+ }
288
+ }, [])
289
+
290
+ const removeToast = React.useCallback((toastToRemove: ToastT) => {
291
+ setToasts((toasts) => {
292
+ if (!toasts.find((toast) => toast.id === toastToRemove.id)?.delete) {
293
+ ToastState.dismiss(toastToRemove.id)
294
+ }
295
+ return toasts.filter(({ id }) => id !== toastToRemove.id)
296
+ })
297
+ }, [])
298
+
299
+ // parse position
300
+ const [yPosition, xPosition] = position.split('-') as [
301
+ 'top' | 'bottom',
302
+ 'left' | 'center' | 'right',
303
+ ]
304
+
305
+ // calculate offset styles
306
+ const offsetStyles = React.useMemo(() => {
307
+ const styles: React.CSSProperties = {}
308
+
309
+ const defaultOffset = typeof offset === 'number' ? offset : VIEWPORT_OFFSET
310
+ const offsetObj =
311
+ typeof offset === 'object'
312
+ ? offset
313
+ : {
314
+ top: defaultOffset,
315
+ right: defaultOffset,
316
+ bottom: defaultOffset,
317
+ left: defaultOffset,
318
+ }
319
+
320
+ if (yPosition === 'top') {
321
+ styles.top = offsetObj.top ?? defaultOffset
322
+ } else {
323
+ styles.bottom = offsetObj.bottom ?? defaultOffset
324
+ }
325
+
326
+ if (xPosition === 'left') {
327
+ styles.left = offsetObj.left ?? defaultOffset
328
+ } else if (xPosition === 'right') {
329
+ styles.right = offsetObj.right ?? defaultOffset
330
+ } else {
331
+ // center
332
+ styles.left = '50%'
333
+ styles.transform = 'translateX(-50%)'
334
+ }
335
+
336
+ return styles
337
+ }, [offset, yPosition, xPosition])
338
+
339
+ // get current theme
340
+ const currentTheme = useThemeName()
341
+ const resolvedTheme =
342
+ themeProp === 'system' || !themeProp
343
+ ? currentTheme?.includes('dark')
344
+ ? 'dark'
345
+ : 'light'
346
+ : themeProp
347
+
348
+ const hotkeyLabel = hotkey.join('+').replace(/Key/g, '').replace(/Digit/g, '')
349
+
350
+ if (toasts.length === 0) {
351
+ return null
352
+ }
353
+
354
+ const content = (
355
+ <ToasterFrame
356
+ ref={listRef}
357
+ width={width}
358
+ aria-label={`${containerAriaLabel} ${hotkeyLabel}`}
359
+ tabIndex={-1}
360
+ aria-live="polite"
361
+ aria-relevant="additions text"
362
+ aria-atomic={false}
363
+ style={{ ...offsetStyles, ...style }}
364
+ className={className}
365
+ data-y-position={yPosition}
366
+ data-x-position={xPosition}
367
+ onMouseEnter={() => setExpanded(true)}
368
+ onMouseMove={() => setExpanded(true)}
369
+ onMouseLeave={() => {
370
+ if (!interacting) {
371
+ setExpanded(false)
372
+ }
373
+ }}
374
+ onPointerDown={() => setInteracting(true)}
375
+ onPointerUp={() => setInteracting(false)}
376
+ {...(isWeb && {
377
+ onBlur: (event: React.FocusEvent) => {
378
+ if (
379
+ isFocusWithinRef.current &&
380
+ !(event.currentTarget as HTMLElement).contains(
381
+ event.relatedTarget as HTMLElement
382
+ )
383
+ ) {
384
+ isFocusWithinRef.current = false
385
+ if (lastFocusedElementRef.current) {
386
+ lastFocusedElementRef.current.focus({ preventScroll: true })
387
+ lastFocusedElementRef.current = null
388
+ }
389
+ }
390
+ },
391
+ onFocus: (event: React.FocusEvent) => {
392
+ if (!isFocusWithinRef.current) {
393
+ isFocusWithinRef.current = true
394
+ lastFocusedElementRef.current = event.relatedTarget as HTMLElement
395
+ }
396
+ },
397
+ })}
398
+ >
399
+ <AnimatePresence>
400
+ {toasts.map((toast, index) => {
401
+ const isVisible = index < visibleToasts
402
+ const isFront = index === 0
403
+
404
+ return (
405
+ <ToastItem
406
+ key={toast.id}
407
+ toast={toast}
408
+ index={index}
409
+ expanded={expanded || expand}
410
+ interacting={interacting}
411
+ position={position}
412
+ visibleToasts={visibleToasts}
413
+ removeToast={removeToast}
414
+ heights={heights}
415
+ setHeights={setHeights}
416
+ duration={toast.duration ?? toastOptions?.duration ?? duration}
417
+ gap={gap}
418
+ swipeDirection={swipeDirection}
419
+ swipeThreshold={swipeThreshold}
420
+ closeButton={toast.closeButton ?? closeButton}
421
+ icons={icons}
422
+ disableNative={disableNative}
423
+ burntOptions={burntOptions}
424
+ notificationOptions={notificationOptions}
425
+ />
426
+ )
427
+ })}
428
+ </AnimatePresence>
429
+ </ToasterFrame>
430
+ )
431
+
432
+ // on web, render in a portal
433
+ if (isWeb) {
434
+ return (
435
+ <Portal>
436
+ <Theme name={resolvedTheme as any}>{content}</Theme>
437
+ </Portal>
438
+ )
439
+ }
440
+
441
+ return <Theme name={resolvedTheme as any}>{content}</Theme>
442
+ }
443
+ )
444
+
445
+ Toaster.displayName = 'Toaster'
@@ -0,0 +1,2 @@
1
+ export const TOAST_NAME = 'Toast'
2
+ export const TOAST_CONTEXT = 'Toast'
@@ -0,0 +1,22 @@
1
+ import type { CreateNativeToastsFn, HideNativeToastsFn } from './types'
2
+
3
+ export const createNativeToast: CreateNativeToastsFn = (
4
+ title,
5
+ { message, duration, burntOptions }
6
+ ) => {
7
+ // import inline to allow lazy usage of native dependecy:
8
+ const Burnt = require('burnt') as typeof import('burnt')
9
+
10
+ Burnt.toast({
11
+ title,
12
+ message,
13
+ duration: duration ? duration / 1000 : undefined,
14
+ ...burntOptions,
15
+ })
16
+ return true
17
+ }
18
+
19
+ export const hideNativeToast: HideNativeToastsFn = () => {
20
+ const Burnt = require('burnt') as typeof import('burnt')
21
+ Burnt.dismissAllAlerts()
22
+ }
@@ -0,0 +1,48 @@
1
+ import type { CreateNativeToastsFn, HideNativeToastsFn } from './types'
2
+
3
+ export const createNativeToast: CreateNativeToastsFn = (
4
+ title,
5
+ { message, notificationOptions }
6
+ ) => {
7
+ if (!('Notification' in window)) {
8
+ console.error('This browser does not support notifications')
9
+ return false
10
+ }
11
+
12
+ if (Notification.permission === 'denied') return false
13
+ const showNotification = () => {
14
+ const notification = new Notification(title, {
15
+ body: message,
16
+ ...notificationOptions,
17
+ })
18
+
19
+ return notification
20
+ }
21
+
22
+ if (Notification.permission === 'granted') {
23
+ const notification = showNotification()
24
+ return {
25
+ nativeToastRef: notification,
26
+ }
27
+ }
28
+ Notification.requestPermission().then((permission) => {
29
+ if (permission === 'granted') {
30
+ const notification = showNotification()
31
+ return {
32
+ nativeToastRef: notification,
33
+ }
34
+ }
35
+ })
36
+ return true
37
+ }
38
+
39
+ export const hideNativeToast: HideNativeToastsFn = (ref) => {
40
+ if (!('Notification' in window)) {
41
+ console.error('This browser does not support notifications')
42
+ return
43
+ }
44
+
45
+ if (ref) {
46
+ ref.close()
47
+ }
48
+ }
package/src/index.ts ADDED
@@ -0,0 +1,17 @@
1
+ // Toast v2 API
2
+ export { toast } from './ToastState'
3
+ export type {
4
+ ToastT,
5
+ ToastType,
6
+ ToastToDismiss,
7
+ ExternalToast,
8
+ PromiseT,
9
+ PromiseData,
10
+ ToastAction,
11
+ } from './ToastState'
12
+
13
+ export { Toaster } from './Toaster'
14
+ export type { ToasterProps, ToasterPosition, HeightT } from './Toaster'
15
+
16
+ export type { SwipeDirection } from './ToastProvider'
17
+ export type { BurntToastOptions, CreateNativeToastOptions, NativeToastRef } from './types'
package/src/types.ts ADDED
@@ -0,0 +1,71 @@
1
+ // from `burnt`
2
+ type BurntLayout = {
3
+ iconSize?: {
4
+ width: number
5
+ height: number
6
+ }
7
+ }
8
+
9
+ // from `burnt`
10
+ export type BurntToastOptions = {
11
+ title: string
12
+ message?: string
13
+ /**
14
+ * Defaults to `done`.
15
+ */
16
+ preset?: 'done' | 'error' | 'none'
17
+ /**
18
+ * Duration in seconds.
19
+ */
20
+ duration?: number
21
+ haptic?: 'success' | 'warning' | 'error' | 'none'
22
+ /**
23
+ * Defaults to `true`.
24
+ */
25
+ shouldDismissByDrag?: boolean
26
+ /**
27
+ * Change the presentation side.
28
+ * @platform ios
29
+ */
30
+ from?: 'top' | 'bottom'
31
+ layout?: BurntLayout
32
+ }
33
+
34
+ export interface CreateNativeToastOptions {
35
+ /**
36
+ * Body of the toast
37
+ */
38
+ message?: BurntToastOptions['message']
39
+ /**
40
+ * Duration of toast in ms
41
+ *
42
+ * @example 1000
43
+ */
44
+ duration?: BurntToastOptions['duration']
45
+ /**
46
+ * Options for the burnt package if you're using native toasts on mobile
47
+ */
48
+ burntOptions?: Omit<BurntToastOptions, 'title' | 'message' | 'duration'>
49
+ /**
50
+ * Options for the notification API if you're using native toasts on web
51
+ */
52
+ notificationOptions?: NotificationOptions
53
+ }
54
+
55
+ export interface NativeToastRef {
56
+ /**
57
+ * Used to close web notifications
58
+ */
59
+ close: () => void
60
+ }
61
+
62
+ export type CreateNativeToastsFn = (
63
+ title: string,
64
+ options: CreateNativeToastOptions
65
+ ) =>
66
+ | {
67
+ nativeToastRef?: NativeToastRef
68
+ }
69
+ | boolean
70
+
71
+ export type HideNativeToastsFn = (ref?: NativeToastRef) => void