@tamagui/v2-toast 2.0.0-1769464493958
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/Toast.cjs +170 -0
- package/dist/cjs/Toast.js +119 -0
- package/dist/cjs/Toast.js.map +6 -0
- package/dist/cjs/Toast.native.js +174 -0
- package/dist/cjs/Toast.native.js.map +1 -0
- package/dist/cjs/ToastAnnounce.cjs +97 -0
- package/dist/cjs/ToastAnnounce.js +72 -0
- package/dist/cjs/ToastAnnounce.js.map +6 -0
- package/dist/cjs/ToastAnnounce.native.js +105 -0
- package/dist/cjs/ToastAnnounce.native.js.map +1 -0
- package/dist/cjs/ToastImperative.cjs +100 -0
- package/dist/cjs/ToastImperative.js +71 -0
- package/dist/cjs/ToastImperative.js.map +6 -0
- package/dist/cjs/ToastImperative.native.js +122 -0
- package/dist/cjs/ToastImperative.native.js.map +1 -0
- package/dist/cjs/ToastImpl.cjs +292 -0
- package/dist/cjs/ToastImpl.js +227 -0
- package/dist/cjs/ToastImpl.js.map +6 -0
- package/dist/cjs/ToastImpl.native.js +327 -0
- package/dist/cjs/ToastImpl.native.js.map +1 -0
- package/dist/cjs/ToastItem.cjs +466 -0
- package/dist/cjs/ToastItem.js +356 -0
- package/dist/cjs/ToastItem.js.map +6 -0
- package/dist/cjs/ToastItem.native.js +547 -0
- package/dist/cjs/ToastItem.native.js.map +1 -0
- package/dist/cjs/ToastPortal.cjs +44 -0
- package/dist/cjs/ToastPortal.js +26 -0
- package/dist/cjs/ToastPortal.js.map +6 -0
- package/dist/cjs/ToastPortal.native.js +47 -0
- package/dist/cjs/ToastPortal.native.js.map +1 -0
- package/dist/cjs/ToastProvider.cjs +146 -0
- package/dist/cjs/ToastProvider.js +105 -0
- package/dist/cjs/ToastProvider.js.map +6 -0
- package/dist/cjs/ToastProvider.native.js +159 -0
- package/dist/cjs/ToastProvider.native.js.map +1 -0
- package/dist/cjs/ToastState.cjs +248 -0
- package/dist/cjs/ToastState.js +160 -0
- package/dist/cjs/ToastState.js.map +6 -0
- package/dist/cjs/ToastState.native.js +257 -0
- package/dist/cjs/ToastState.native.js.map +1 -0
- package/dist/cjs/ToastViewport.cjs +278 -0
- package/dist/cjs/ToastViewport.js +263 -0
- package/dist/cjs/ToastViewport.js.map +6 -0
- package/dist/cjs/ToastViewport.native.js +316 -0
- package/dist/cjs/ToastViewport.native.js.map +1 -0
- package/dist/cjs/Toaster.cjs +219 -0
- package/dist/cjs/Toaster.js +177 -0
- package/dist/cjs/Toaster.js.map +6 -0
- package/dist/cjs/Toaster.native.js +279 -0
- package/dist/cjs/Toaster.native.js.map +1 -0
- package/dist/cjs/constants.cjs +28 -0
- package/dist/cjs/constants.js +22 -0
- package/dist/cjs/constants.js.map +6 -0
- package/dist/cjs/constants.native.js +31 -0
- package/dist/cjs/constants.native.js.map +1 -0
- package/dist/cjs/createNativeToast.cjs +51 -0
- package/dist/cjs/createNativeToast.js +44 -0
- package/dist/cjs/createNativeToast.js.map +6 -0
- package/dist/cjs/createNativeToast.native.js +47 -0
- package/dist/cjs/createNativeToast.native.js.map +1 -0
- package/dist/cjs/index.cjs +28 -0
- package/dist/cjs/index.js +22 -0
- package/dist/cjs/index.js.map +6 -0
- package/dist/cjs/index.native.js +31 -0
- package/dist/cjs/index.native.js.map +1 -0
- package/dist/cjs/types.cjs +16 -0
- package/dist/cjs/types.js +14 -0
- package/dist/cjs/types.js.map +6 -0
- package/dist/cjs/types.native.js +19 -0
- package/dist/cjs/types.native.js.map +1 -0
- package/dist/cjs/useDragGesture.cjs +129 -0
- package/dist/cjs/useDragGesture.js +100 -0
- package/dist/cjs/useDragGesture.js.map +6 -0
- package/dist/cjs/useDragGesture.native.js +146 -0
- package/dist/cjs/useDragGesture.native.js.map +1 -0
- package/dist/esm/Toast.js +107 -0
- package/dist/esm/Toast.js.map +6 -0
- package/dist/esm/Toast.mjs +131 -0
- package/dist/esm/Toast.mjs.map +1 -0
- package/dist/esm/Toast.native.js +132 -0
- package/dist/esm/Toast.native.js.map +1 -0
- package/dist/esm/ToastAnnounce.js +55 -0
- package/dist/esm/ToastAnnounce.js.map +6 -0
- package/dist/esm/ToastAnnounce.mjs +62 -0
- package/dist/esm/ToastAnnounce.mjs.map +1 -0
- package/dist/esm/ToastAnnounce.native.js +67 -0
- package/dist/esm/ToastAnnounce.native.js.map +1 -0
- package/dist/esm/ToastImperative.js +50 -0
- package/dist/esm/ToastImperative.js.map +6 -0
- package/dist/esm/ToastImperative.mjs +63 -0
- package/dist/esm/ToastImperative.mjs.map +1 -0
- package/dist/esm/ToastImperative.native.js +82 -0
- package/dist/esm/ToastImperative.native.js.map +1 -0
- package/dist/esm/ToastImpl.js +225 -0
- package/dist/esm/ToastImpl.js.map +6 -0
- package/dist/esm/ToastImpl.mjs +256 -0
- package/dist/esm/ToastImpl.mjs.map +1 -0
- package/dist/esm/ToastImpl.native.js +288 -0
- package/dist/esm/ToastImpl.native.js.map +1 -0
- package/dist/esm/ToastItem.js +339 -0
- package/dist/esm/ToastItem.js.map +6 -0
- package/dist/esm/ToastItem.mjs +432 -0
- package/dist/esm/ToastItem.mjs.map +1 -0
- package/dist/esm/ToastItem.native.js +510 -0
- package/dist/esm/ToastItem.native.js.map +1 -0
- package/dist/esm/ToastPortal.js +13 -0
- package/dist/esm/ToastPortal.js.map +6 -0
- package/dist/esm/ToastPortal.mjs +21 -0
- package/dist/esm/ToastPortal.mjs.map +1 -0
- package/dist/esm/ToastPortal.native.js +21 -0
- package/dist/esm/ToastPortal.native.js.map +1 -0
- package/dist/esm/ToastProvider.js +87 -0
- package/dist/esm/ToastProvider.js.map +6 -0
- package/dist/esm/ToastProvider.mjs +108 -0
- package/dist/esm/ToastProvider.mjs.map +1 -0
- package/dist/esm/ToastProvider.native.js +118 -0
- package/dist/esm/ToastProvider.native.js.map +1 -0
- package/dist/esm/ToastState.js +144 -0
- package/dist/esm/ToastState.js.map +6 -0
- package/dist/esm/ToastState.mjs +224 -0
- package/dist/esm/ToastState.mjs.map +1 -0
- package/dist/esm/ToastState.native.js +230 -0
- package/dist/esm/ToastState.native.js.map +1 -0
- package/dist/esm/ToastViewport.js +250 -0
- package/dist/esm/ToastViewport.js.map +6 -0
- package/dist/esm/ToastViewport.mjs +241 -0
- package/dist/esm/ToastViewport.mjs.map +1 -0
- package/dist/esm/ToastViewport.native.js +276 -0
- package/dist/esm/ToastViewport.native.js.map +1 -0
- package/dist/esm/Toaster.js +160 -0
- package/dist/esm/Toaster.js.map +6 -0
- package/dist/esm/Toaster.mjs +185 -0
- package/dist/esm/Toaster.mjs.map +1 -0
- package/dist/esm/Toaster.native.js +242 -0
- package/dist/esm/Toaster.native.js.map +1 -0
- package/dist/esm/constants.js +6 -0
- package/dist/esm/constants.js.map +6 -0
- package/dist/esm/constants.mjs +4 -0
- package/dist/esm/constants.mjs.map +1 -0
- package/dist/esm/constants.native.js +4 -0
- package/dist/esm/constants.native.js.map +1 -0
- package/dist/esm/createNativeToast.js +28 -0
- package/dist/esm/createNativeToast.js.map +6 -0
- package/dist/esm/createNativeToast.mjs +27 -0
- package/dist/esm/createNativeToast.mjs.map +1 -0
- package/dist/esm/createNativeToast.native.js +20 -0
- package/dist/esm/createNativeToast.native.js.map +1 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +6 -0
- package/dist/esm/index.mjs +4 -0
- package/dist/esm/index.mjs.map +1 -0
- package/dist/esm/index.native.js +4 -0
- package/dist/esm/index.native.js.map +1 -0
- package/dist/esm/types.js +1 -0
- package/dist/esm/types.js.map +6 -0
- package/dist/esm/types.mjs +2 -0
- package/dist/esm/types.mjs.map +1 -0
- package/dist/esm/types.native.js +2 -0
- package/dist/esm/types.native.js.map +1 -0
- package/dist/esm/useDragGesture.js +76 -0
- package/dist/esm/useDragGesture.js.map +6 -0
- package/dist/esm/useDragGesture.mjs +95 -0
- package/dist/esm/useDragGesture.mjs.map +1 -0
- package/dist/esm/useDragGesture.native.js +109 -0
- package/dist/esm/useDragGesture.native.js.map +1 -0
- package/dist/jsx/Toast.js +107 -0
- package/dist/jsx/Toast.js.map +6 -0
- package/dist/jsx/Toast.mjs +131 -0
- package/dist/jsx/Toast.mjs.map +1 -0
- package/dist/jsx/Toast.native.js +174 -0
- package/dist/jsx/Toast.native.js.map +1 -0
- package/dist/jsx/ToastAnnounce.js +55 -0
- package/dist/jsx/ToastAnnounce.js.map +6 -0
- package/dist/jsx/ToastAnnounce.mjs +62 -0
- package/dist/jsx/ToastAnnounce.mjs.map +1 -0
- package/dist/jsx/ToastAnnounce.native.js +105 -0
- package/dist/jsx/ToastAnnounce.native.js.map +1 -0
- package/dist/jsx/ToastImperative.js +50 -0
- package/dist/jsx/ToastImperative.js.map +6 -0
- package/dist/jsx/ToastImperative.mjs +63 -0
- package/dist/jsx/ToastImperative.mjs.map +1 -0
- package/dist/jsx/ToastImperative.native.js +122 -0
- package/dist/jsx/ToastImperative.native.js.map +1 -0
- package/dist/jsx/ToastImpl.js +225 -0
- package/dist/jsx/ToastImpl.js.map +6 -0
- package/dist/jsx/ToastImpl.mjs +256 -0
- package/dist/jsx/ToastImpl.mjs.map +1 -0
- package/dist/jsx/ToastImpl.native.js +327 -0
- package/dist/jsx/ToastImpl.native.js.map +1 -0
- package/dist/jsx/ToastItem.js +339 -0
- package/dist/jsx/ToastItem.js.map +6 -0
- package/dist/jsx/ToastItem.mjs +432 -0
- package/dist/jsx/ToastItem.mjs.map +1 -0
- package/dist/jsx/ToastItem.native.js +547 -0
- package/dist/jsx/ToastItem.native.js.map +1 -0
- package/dist/jsx/ToastPortal.js +13 -0
- package/dist/jsx/ToastPortal.js.map +6 -0
- package/dist/jsx/ToastPortal.mjs +21 -0
- package/dist/jsx/ToastPortal.mjs.map +1 -0
- package/dist/jsx/ToastPortal.native.js +47 -0
- package/dist/jsx/ToastPortal.native.js.map +1 -0
- package/dist/jsx/ToastProvider.js +87 -0
- package/dist/jsx/ToastProvider.js.map +6 -0
- package/dist/jsx/ToastProvider.mjs +108 -0
- package/dist/jsx/ToastProvider.mjs.map +1 -0
- package/dist/jsx/ToastProvider.native.js +159 -0
- package/dist/jsx/ToastProvider.native.js.map +1 -0
- package/dist/jsx/ToastState.js +144 -0
- package/dist/jsx/ToastState.js.map +6 -0
- package/dist/jsx/ToastState.mjs +224 -0
- package/dist/jsx/ToastState.mjs.map +1 -0
- package/dist/jsx/ToastState.native.js +257 -0
- package/dist/jsx/ToastState.native.js.map +1 -0
- package/dist/jsx/ToastViewport.js +250 -0
- package/dist/jsx/ToastViewport.js.map +6 -0
- package/dist/jsx/ToastViewport.mjs +241 -0
- package/dist/jsx/ToastViewport.mjs.map +1 -0
- package/dist/jsx/ToastViewport.native.js +316 -0
- package/dist/jsx/ToastViewport.native.js.map +1 -0
- package/dist/jsx/Toaster.js +160 -0
- package/dist/jsx/Toaster.js.map +6 -0
- package/dist/jsx/Toaster.mjs +185 -0
- package/dist/jsx/Toaster.mjs.map +1 -0
- package/dist/jsx/Toaster.native.js +279 -0
- package/dist/jsx/Toaster.native.js.map +1 -0
- package/dist/jsx/constants.js +6 -0
- package/dist/jsx/constants.js.map +6 -0
- package/dist/jsx/constants.mjs +4 -0
- package/dist/jsx/constants.mjs.map +1 -0
- package/dist/jsx/constants.native.js +31 -0
- package/dist/jsx/constants.native.js.map +1 -0
- package/dist/jsx/createNativeToast.js +28 -0
- package/dist/jsx/createNativeToast.js.map +6 -0
- package/dist/jsx/createNativeToast.mjs +27 -0
- package/dist/jsx/createNativeToast.mjs.map +1 -0
- package/dist/jsx/createNativeToast.native.js +47 -0
- package/dist/jsx/createNativeToast.native.js.map +1 -0
- package/dist/jsx/index.js +7 -0
- package/dist/jsx/index.js.map +6 -0
- package/dist/jsx/index.mjs +4 -0
- package/dist/jsx/index.mjs.map +1 -0
- package/dist/jsx/index.native.js +31 -0
- package/dist/jsx/index.native.js.map +1 -0
- package/dist/jsx/types.js +1 -0
- package/dist/jsx/types.js.map +6 -0
- package/dist/jsx/types.mjs +2 -0
- package/dist/jsx/types.mjs.map +1 -0
- package/dist/jsx/types.native.js +19 -0
- package/dist/jsx/types.native.js.map +1 -0
- package/dist/jsx/useDragGesture.js +76 -0
- package/dist/jsx/useDragGesture.js.map +6 -0
- package/dist/jsx/useDragGesture.mjs +95 -0
- package/dist/jsx/useDragGesture.mjs.map +1 -0
- package/dist/jsx/useDragGesture.native.js +146 -0
- package/dist/jsx/useDragGesture.native.js.map +1 -0
- package/package.json +77 -0
- package/src/Toast.tsx +219 -0
- package/src/ToastAnnounce.tsx +102 -0
- package/src/ToastImperative.tsx +190 -0
- package/src/ToastImpl.tsx +503 -0
- package/src/ToastItem.tsx +694 -0
- package/src/ToastPortal.tsx +19 -0
- package/src/ToastProvider.tsx +197 -0
- package/src/ToastState.ts +397 -0
- package/src/ToastViewport.tsx +430 -0
- package/src/Toaster.tsx +445 -0
- package/src/constants.ts +2 -0
- package/src/createNativeToast.native.tsx +22 -0
- package/src/createNativeToast.tsx +48 -0
- package/src/index.ts +17 -0
- package/src/types.ts +71 -0
- package/src/useDragGesture.native.ts +199 -0
- package/src/useDragGesture.ts +218 -0
- package/types/Toast.d.ts +84 -0
- package/types/Toast.d.ts.map +1 -0
- package/types/ToastAnnounce.d.ts +18 -0
- package/types/ToastAnnounce.d.ts.map +1 -0
- package/types/ToastImperative.d.ts +95 -0
- package/types/ToastImperative.d.ts.map +1 -0
- package/types/ToastImpl.d.ts +109 -0
- package/types/ToastImpl.d.ts.map +1 -0
- package/types/ToastItem.d.ts +34 -0
- package/types/ToastItem.d.ts.map +1 -0
- package/types/ToastPortal.d.ts +8 -0
- package/types/ToastPortal.d.ts.map +1 -0
- package/types/ToastProvider.d.ts +92 -0
- package/types/ToastProvider.d.ts.map +1 -0
- package/types/ToastState.d.ts +177 -0
- package/types/ToastState.d.ts.map +1 -0
- package/types/ToastViewport.d.ts +75 -0
- package/types/ToastViewport.d.ts.map +1 -0
- package/types/Toaster.d.ts +120 -0
- package/types/Toaster.d.ts.map +1 -0
- package/types/constants.d.ts +3 -0
- package/types/constants.d.ts.map +1 -0
- package/types/createNativeToast.d.ts +4 -0
- package/types/createNativeToast.d.ts.map +1 -0
- package/types/createNativeToast.native.d.ts +4 -0
- package/types/createNativeToast.native.d.ts.map +1 -0
- package/types/index.d.ts +7 -0
- package/types/index.d.ts.map +1 -0
- package/types/types.d.ts +61 -0
- package/types/types.d.ts.map +1 -0
- package/types/useDragGesture.d.ts +32 -0
- package/types/useDragGesture.d.ts.map +1 -0
- package/types/useDragGesture.native.d.ts +26 -0
- package/types/useDragGesture.native.d.ts.map +1 -0
|
@@ -0,0 +1,694 @@
|
|
|
1
|
+
import { isWeb } from '@tamagui/constants'
|
|
2
|
+
import type { TamaguiElement } from '@tamagui/core'
|
|
3
|
+
import { styled, useEvent, View } from '@tamagui/core'
|
|
4
|
+
import { XStack, YStack } from '@tamagui/stacks'
|
|
5
|
+
import { SizableText } from '@tamagui/text'
|
|
6
|
+
import * as React from 'react'
|
|
7
|
+
import type { LayoutChangeEvent } from 'react-native'
|
|
8
|
+
|
|
9
|
+
import type { HeightT, ToasterPosition } from './Toaster'
|
|
10
|
+
import type { ToastT, ToastType } from './ToastState'
|
|
11
|
+
import { useDragGesture } from './useDragGesture'
|
|
12
|
+
import type { SwipeDirection } from './ToastProvider'
|
|
13
|
+
import type { BurntToastOptions } from './types'
|
|
14
|
+
import { createNativeToast } from './createNativeToast'
|
|
15
|
+
|
|
16
|
+
// time before unmount after deletion
|
|
17
|
+
const TIME_BEFORE_UNMOUNT = 200
|
|
18
|
+
|
|
19
|
+
/* -------------------------------------------------------------------------------------------------
|
|
20
|
+
* ToastItemFrame
|
|
21
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
22
|
+
|
|
23
|
+
const ToastItemFrame = styled(YStack, {
|
|
24
|
+
name: 'ToastItem',
|
|
25
|
+
focusable: true,
|
|
26
|
+
pointerEvents: 'auto',
|
|
27
|
+
position: 'absolute',
|
|
28
|
+
left: 0,
|
|
29
|
+
right: 0,
|
|
30
|
+
// for stacking animation - default visible state
|
|
31
|
+
opacity: 1,
|
|
32
|
+
scale: 1,
|
|
33
|
+
y: 0,
|
|
34
|
+
x: 0,
|
|
35
|
+
|
|
36
|
+
variants: {
|
|
37
|
+
unstyled: {
|
|
38
|
+
false: {
|
|
39
|
+
backgroundColor: '$background',
|
|
40
|
+
borderRadius: '$4',
|
|
41
|
+
paddingHorizontal: '$4',
|
|
42
|
+
paddingVertical: '$3',
|
|
43
|
+
// shadow using elevation for cross-platform
|
|
44
|
+
elevation: '$4',
|
|
45
|
+
shadowColor: '$shadowColor',
|
|
46
|
+
shadowOffset: { width: 0, height: 4 },
|
|
47
|
+
shadowOpacity: 0.15,
|
|
48
|
+
shadowRadius: 12,
|
|
49
|
+
borderWidth: 1,
|
|
50
|
+
borderColor: '$borderColor',
|
|
51
|
+
|
|
52
|
+
// only show focus outline on keyboard navigation, not on click/tap
|
|
53
|
+
focusVisibleStyle: {
|
|
54
|
+
outlineStyle: 'solid',
|
|
55
|
+
outlineWidth: 2,
|
|
56
|
+
outlineColor: '$outlineColor',
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
} as const,
|
|
61
|
+
|
|
62
|
+
defaultVariants: {
|
|
63
|
+
unstyled: process.env.TAMAGUI_HEADLESS === '1',
|
|
64
|
+
},
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
/* -------------------------------------------------------------------------------------------------
|
|
68
|
+
* ToastTitle
|
|
69
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
70
|
+
|
|
71
|
+
const ToastItemTitle = styled(SizableText, {
|
|
72
|
+
name: 'ToastItemTitle',
|
|
73
|
+
|
|
74
|
+
variants: {
|
|
75
|
+
unstyled: {
|
|
76
|
+
false: {
|
|
77
|
+
fontWeight: '600',
|
|
78
|
+
color: '$color',
|
|
79
|
+
size: '$4',
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
} as const,
|
|
83
|
+
|
|
84
|
+
defaultVariants: {
|
|
85
|
+
unstyled: process.env.TAMAGUI_HEADLESS === '1',
|
|
86
|
+
},
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
/* -------------------------------------------------------------------------------------------------
|
|
90
|
+
* ToastDescription
|
|
91
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
92
|
+
|
|
93
|
+
const ToastItemDescription = styled(SizableText, {
|
|
94
|
+
name: 'ToastItemDescription',
|
|
95
|
+
|
|
96
|
+
variants: {
|
|
97
|
+
unstyled: {
|
|
98
|
+
false: {
|
|
99
|
+
color: '$color11',
|
|
100
|
+
size: '$2',
|
|
101
|
+
marginTop: '$1',
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
} as const,
|
|
105
|
+
|
|
106
|
+
defaultVariants: {
|
|
107
|
+
unstyled: process.env.TAMAGUI_HEADLESS === '1',
|
|
108
|
+
},
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
/* -------------------------------------------------------------------------------------------------
|
|
112
|
+
* ToastCloseButton
|
|
113
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
114
|
+
|
|
115
|
+
const ToastCloseButton = styled(XStack, {
|
|
116
|
+
name: 'ToastCloseButton',
|
|
117
|
+
render: 'button',
|
|
118
|
+
alignItems: 'center',
|
|
119
|
+
justifyContent: 'center',
|
|
120
|
+
cursor: 'pointer',
|
|
121
|
+
|
|
122
|
+
variants: {
|
|
123
|
+
unstyled: {
|
|
124
|
+
false: {
|
|
125
|
+
width: 20,
|
|
126
|
+
height: 20,
|
|
127
|
+
borderRadius: '$10',
|
|
128
|
+
backgroundColor: '$color5',
|
|
129
|
+
hoverStyle: {
|
|
130
|
+
backgroundColor: '$color6',
|
|
131
|
+
},
|
|
132
|
+
pressStyle: {
|
|
133
|
+
backgroundColor: '$color7',
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
} as const,
|
|
138
|
+
|
|
139
|
+
defaultVariants: {
|
|
140
|
+
unstyled: process.env.TAMAGUI_HEADLESS === '1',
|
|
141
|
+
},
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
/* -------------------------------------------------------------------------------------------------
|
|
145
|
+
* ToastActionButton - for action/cancel buttons with text
|
|
146
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
147
|
+
|
|
148
|
+
const ToastActionButton = styled(XStack, {
|
|
149
|
+
name: 'ToastActionButton',
|
|
150
|
+
render: 'button',
|
|
151
|
+
alignItems: 'center',
|
|
152
|
+
justifyContent: 'center',
|
|
153
|
+
cursor: 'pointer',
|
|
154
|
+
|
|
155
|
+
variants: {
|
|
156
|
+
unstyled: {
|
|
157
|
+
false: {
|
|
158
|
+
borderRadius: '$2',
|
|
159
|
+
paddingHorizontal: '$2',
|
|
160
|
+
height: 24,
|
|
161
|
+
backgroundColor: '$color5',
|
|
162
|
+
hoverStyle: {
|
|
163
|
+
backgroundColor: '$color6',
|
|
164
|
+
},
|
|
165
|
+
pressStyle: {
|
|
166
|
+
backgroundColor: '$color7',
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
// primary action button style
|
|
172
|
+
primary: {
|
|
173
|
+
true: {
|
|
174
|
+
backgroundColor: '$color12',
|
|
175
|
+
hoverStyle: {
|
|
176
|
+
backgroundColor: '$color11',
|
|
177
|
+
},
|
|
178
|
+
pressStyle: {
|
|
179
|
+
backgroundColor: '$color10',
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
} as const,
|
|
184
|
+
|
|
185
|
+
defaultVariants: {
|
|
186
|
+
unstyled: process.env.TAMAGUI_HEADLESS === '1',
|
|
187
|
+
},
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
/* -------------------------------------------------------------------------------------------------
|
|
191
|
+
* Icons
|
|
192
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
193
|
+
|
|
194
|
+
const DefaultCloseIcon = () => (
|
|
195
|
+
<SizableText size="$1" color="$color11">
|
|
196
|
+
✕
|
|
197
|
+
</SizableText>
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
const DefaultSuccessIcon = () => (
|
|
201
|
+
<SizableText size="$5" color="$green10">
|
|
202
|
+
✓
|
|
203
|
+
</SizableText>
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
const DefaultErrorIcon = () => (
|
|
207
|
+
<SizableText size="$5" color="$red10">
|
|
208
|
+
✕
|
|
209
|
+
</SizableText>
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
const DefaultWarningIcon = () => (
|
|
213
|
+
<SizableText size="$5" color="$yellow10">
|
|
214
|
+
⚠
|
|
215
|
+
</SizableText>
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
const DefaultInfoIcon = () => (
|
|
219
|
+
<SizableText size="$5" color="$blue10">
|
|
220
|
+
ℹ
|
|
221
|
+
</SizableText>
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
const DefaultLoadingIcon = () => (
|
|
225
|
+
<SizableText size="$5" color="$color11">
|
|
226
|
+
⟳
|
|
227
|
+
</SizableText>
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
/* -------------------------------------------------------------------------------------------------
|
|
231
|
+
* ToastItem
|
|
232
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
233
|
+
|
|
234
|
+
export interface ToastItemProps {
|
|
235
|
+
toast: ToastT
|
|
236
|
+
index: number
|
|
237
|
+
expanded: boolean
|
|
238
|
+
interacting: boolean
|
|
239
|
+
position: ToasterPosition
|
|
240
|
+
visibleToasts: number
|
|
241
|
+
removeToast: (toast: ToastT) => void
|
|
242
|
+
heights: HeightT[]
|
|
243
|
+
setHeights: React.Dispatch<React.SetStateAction<HeightT[]>>
|
|
244
|
+
duration: number
|
|
245
|
+
gap: number
|
|
246
|
+
swipeDirection: SwipeDirection
|
|
247
|
+
swipeThreshold: number
|
|
248
|
+
closeButton?: boolean
|
|
249
|
+
icons?: {
|
|
250
|
+
success?: React.ReactNode
|
|
251
|
+
error?: React.ReactNode
|
|
252
|
+
warning?: React.ReactNode
|
|
253
|
+
info?: React.ReactNode
|
|
254
|
+
loading?: React.ReactNode
|
|
255
|
+
close?: React.ReactNode
|
|
256
|
+
}
|
|
257
|
+
disableNative?: boolean
|
|
258
|
+
burntOptions?: Omit<BurntToastOptions, 'title' | 'message' | 'duration'>
|
|
259
|
+
notificationOptions?: NotificationOptions
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export const ToastItem = React.memo(function ToastItem(props: ToastItemProps) {
|
|
263
|
+
const {
|
|
264
|
+
toast,
|
|
265
|
+
index,
|
|
266
|
+
expanded,
|
|
267
|
+
interacting,
|
|
268
|
+
position,
|
|
269
|
+
visibleToasts,
|
|
270
|
+
removeToast,
|
|
271
|
+
heights,
|
|
272
|
+
setHeights,
|
|
273
|
+
duration,
|
|
274
|
+
gap,
|
|
275
|
+
swipeDirection,
|
|
276
|
+
swipeThreshold,
|
|
277
|
+
closeButton,
|
|
278
|
+
icons,
|
|
279
|
+
disableNative,
|
|
280
|
+
burntOptions,
|
|
281
|
+
notificationOptions,
|
|
282
|
+
} = props
|
|
283
|
+
|
|
284
|
+
const [mounted, setMounted] = React.useState(false)
|
|
285
|
+
const [removed, setRemoved] = React.useState(false)
|
|
286
|
+
const [swipeOut, setSwipeOut] = React.useState(false)
|
|
287
|
+
|
|
288
|
+
const toastRef = React.useRef<TamaguiElement>(null)
|
|
289
|
+
const closeTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null)
|
|
290
|
+
const closeTimerStartRef = React.useRef(0)
|
|
291
|
+
const lastPauseTimeRef = React.useRef(0) // tracks when pause was called to prevent double-counting
|
|
292
|
+
const remainingTimeRef = React.useRef(duration)
|
|
293
|
+
|
|
294
|
+
const isFront = index === 0
|
|
295
|
+
const isVisible = index < visibleToasts
|
|
296
|
+
const toastType = toast.type ?? 'default'
|
|
297
|
+
const dismissible = toast.dismissible !== false
|
|
298
|
+
|
|
299
|
+
// calculate offset based on heights of previous toasts
|
|
300
|
+
const heightIndex = React.useMemo(
|
|
301
|
+
() => heights.findIndex((h) => h.toastId === toast.id) || 0,
|
|
302
|
+
[heights, toast.id]
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
const toastsHeightBefore = React.useMemo(() => {
|
|
306
|
+
return heights.reduce((prev, curr, reducerIndex) => {
|
|
307
|
+
if (reducerIndex >= heightIndex) return prev
|
|
308
|
+
return prev + curr.height
|
|
309
|
+
}, 0)
|
|
310
|
+
}, [heights, heightIndex])
|
|
311
|
+
|
|
312
|
+
const offset = heightIndex * gap + toastsHeightBefore
|
|
313
|
+
|
|
314
|
+
// parse position for animation direction
|
|
315
|
+
const [yPosition] = position.split('-') as ['top' | 'bottom', string]
|
|
316
|
+
const isTop = yPosition === 'top'
|
|
317
|
+
|
|
318
|
+
// handle native toast on mobile
|
|
319
|
+
React.useEffect(() => {
|
|
320
|
+
if (!disableNative && !isWeb) {
|
|
321
|
+
const titleText = typeof toast.title === 'function' ? toast.title() : toast.title
|
|
322
|
+
const descText =
|
|
323
|
+
typeof toast.description === 'function' ? toast.description() : toast.description
|
|
324
|
+
|
|
325
|
+
if (typeof titleText === 'string') {
|
|
326
|
+
createNativeToast(titleText, {
|
|
327
|
+
message: typeof descText === 'string' ? descText : undefined,
|
|
328
|
+
duration,
|
|
329
|
+
burntOptions,
|
|
330
|
+
notificationOptions,
|
|
331
|
+
})
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}, [])
|
|
335
|
+
|
|
336
|
+
// trigger mount animation
|
|
337
|
+
React.useEffect(() => {
|
|
338
|
+
setMounted(true)
|
|
339
|
+
}, [])
|
|
340
|
+
|
|
341
|
+
// handle deletion
|
|
342
|
+
React.useEffect(() => {
|
|
343
|
+
if (toast.delete) {
|
|
344
|
+
setRemoved(true)
|
|
345
|
+
setTimeout(() => {
|
|
346
|
+
removeToast(toast)
|
|
347
|
+
}, TIME_BEFORE_UNMOUNT)
|
|
348
|
+
}
|
|
349
|
+
}, [toast.delete, toast, removeToast])
|
|
350
|
+
|
|
351
|
+
// auto-close timer
|
|
352
|
+
const startTimer = React.useCallback(() => {
|
|
353
|
+
if (duration === Number.POSITIVE_INFINITY || toastType === 'loading') return
|
|
354
|
+
|
|
355
|
+
closeTimerStartRef.current = Date.now()
|
|
356
|
+
closeTimerRef.current = setTimeout(() => {
|
|
357
|
+
toast.onAutoClose?.(toast)
|
|
358
|
+
setRemoved(true)
|
|
359
|
+
setTimeout(() => {
|
|
360
|
+
removeToast(toast)
|
|
361
|
+
}, TIME_BEFORE_UNMOUNT)
|
|
362
|
+
}, remainingTimeRef.current)
|
|
363
|
+
}, [duration, toastType, toast, removeToast])
|
|
364
|
+
|
|
365
|
+
const pauseTimer = useEvent(() => {
|
|
366
|
+
if (closeTimerRef.current) {
|
|
367
|
+
clearTimeout(closeTimerRef.current)
|
|
368
|
+
}
|
|
369
|
+
// only calculate elapsed time if timer was started after last pause
|
|
370
|
+
// this prevents double-counting when pause is called multiple times (Sonner pattern)
|
|
371
|
+
if (lastPauseTimeRef.current < closeTimerStartRef.current) {
|
|
372
|
+
const elapsed = Date.now() - closeTimerStartRef.current
|
|
373
|
+
remainingTimeRef.current = Math.max(0, remainingTimeRef.current - elapsed)
|
|
374
|
+
}
|
|
375
|
+
lastPauseTimeRef.current = Date.now()
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
const resumeTimer = useEvent(() => {
|
|
379
|
+
startTimer()
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
// start/pause timer based on expanded/interacting state
|
|
383
|
+
React.useEffect(() => {
|
|
384
|
+
if (expanded || interacting) {
|
|
385
|
+
pauseTimer()
|
|
386
|
+
} else {
|
|
387
|
+
startTimer()
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return () => {
|
|
391
|
+
if (closeTimerRef.current) {
|
|
392
|
+
clearTimeout(closeTimerRef.current)
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}, [expanded, interacting, startTimer])
|
|
396
|
+
|
|
397
|
+
// reset remaining time when duration changes
|
|
398
|
+
React.useEffect(() => {
|
|
399
|
+
remainingTimeRef.current = duration
|
|
400
|
+
}, [duration])
|
|
401
|
+
|
|
402
|
+
// drag gesture for swipe-to-dismiss
|
|
403
|
+
const { dragState, gestureHandlers } = useDragGesture({
|
|
404
|
+
direction: swipeDirection,
|
|
405
|
+
threshold: swipeThreshold,
|
|
406
|
+
disabled: !dismissible || toastType === 'loading',
|
|
407
|
+
onDragStart: pauseTimer,
|
|
408
|
+
onDragEnd: (dismissed) => {
|
|
409
|
+
if (dismissed) {
|
|
410
|
+
setSwipeOut(true)
|
|
411
|
+
toast.onDismiss?.(toast)
|
|
412
|
+
setRemoved(true)
|
|
413
|
+
setTimeout(() => {
|
|
414
|
+
removeToast(toast)
|
|
415
|
+
}, TIME_BEFORE_UNMOUNT)
|
|
416
|
+
}
|
|
417
|
+
},
|
|
418
|
+
onDragCancel: resumeTimer,
|
|
419
|
+
})
|
|
420
|
+
|
|
421
|
+
// measure height
|
|
422
|
+
const handleLayout = React.useCallback(
|
|
423
|
+
(event: LayoutChangeEvent) => {
|
|
424
|
+
const { height } = event.nativeEvent.layout
|
|
425
|
+
|
|
426
|
+
setHeights((prev) => {
|
|
427
|
+
const exists = prev.find((h) => h.toastId === toast.id)
|
|
428
|
+
if (exists) {
|
|
429
|
+
return prev.map((h) => (h.toastId === toast.id ? { ...h, height } : h))
|
|
430
|
+
}
|
|
431
|
+
return [{ toastId: toast.id, height, position }, ...prev]
|
|
432
|
+
})
|
|
433
|
+
},
|
|
434
|
+
[toast.id, position, setHeights]
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
// cleanup height on unmount
|
|
438
|
+
React.useEffect(() => {
|
|
439
|
+
return () => {
|
|
440
|
+
setHeights((prev) => prev.filter((h) => h.toastId !== toast.id))
|
|
441
|
+
}
|
|
442
|
+
}, [toast.id, setHeights])
|
|
443
|
+
|
|
444
|
+
const handleClose = React.useCallback(() => {
|
|
445
|
+
if (!dismissible) return
|
|
446
|
+
toast.onDismiss?.(toast)
|
|
447
|
+
setRemoved(true)
|
|
448
|
+
setTimeout(() => {
|
|
449
|
+
removeToast(toast)
|
|
450
|
+
}, TIME_BEFORE_UNMOUNT)
|
|
451
|
+
}, [dismissible, toast, removeToast])
|
|
452
|
+
|
|
453
|
+
// get icon - just use what's passed on the toast, or type-based defaults
|
|
454
|
+
const getIcon = () => {
|
|
455
|
+
if (toast.icon !== undefined) return toast.icon
|
|
456
|
+
|
|
457
|
+
const typeIcons: Record<ToastType, React.ReactNode> = {
|
|
458
|
+
default: null,
|
|
459
|
+
success: icons?.success ?? <DefaultSuccessIcon />,
|
|
460
|
+
error: icons?.error ?? <DefaultErrorIcon />,
|
|
461
|
+
warning: icons?.warning ?? <DefaultWarningIcon />,
|
|
462
|
+
info: icons?.info ?? <DefaultInfoIcon />,
|
|
463
|
+
loading: icons?.loading ?? <DefaultLoadingIcon />,
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
return typeIcons[toastType]
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
const icon = getIcon()
|
|
470
|
+
|
|
471
|
+
// calculate values for stacking effect using Tamagui animation props
|
|
472
|
+
const isHorizontalSwipe =
|
|
473
|
+
swipeDirection === 'left' ||
|
|
474
|
+
swipeDirection === 'right' ||
|
|
475
|
+
swipeDirection === 'horizontal'
|
|
476
|
+
const isVerticalSwipe =
|
|
477
|
+
swipeDirection === 'up' || swipeDirection === 'down' || swipeDirection === 'vertical'
|
|
478
|
+
|
|
479
|
+
// drag offset based on swipe direction
|
|
480
|
+
const dragOffsetX = isHorizontalSwipe ? dragState.offsetX : 0
|
|
481
|
+
const dragOffsetY = isVerticalSwipe ? dragState.offsetY : 0
|
|
482
|
+
|
|
483
|
+
// scale non-front toasts when not expanded (sonner-style stacking)
|
|
484
|
+
// each toast behind front is scaled down by 5% - this creates visual depth
|
|
485
|
+
const stackScale = !expanded && !isFront ? 1 - index * 0.05 : 1
|
|
486
|
+
|
|
487
|
+
// get the height of the front toast for collapsed positioning
|
|
488
|
+
const frontToastHeight = heights.length > 0 ? (heights[0]?.height ?? 55) : 55
|
|
489
|
+
|
|
490
|
+
// y position: expanded shows full offset, collapsed stacks visually
|
|
491
|
+
// sonner uses gap (14px) as the lift amount per toast in collapsed mode
|
|
492
|
+
const baseOffset = isTop ? offset : -offset
|
|
493
|
+
|
|
494
|
+
// in collapsed mode, create visual stack where each toast peeks behind the one in front
|
|
495
|
+
// for bottom position with transformOrigin: bottom, back toasts need to move UP
|
|
496
|
+
// enough that their TOP edge peeks ABOVE the front toast's TOP edge
|
|
497
|
+
// since scale shrinks toward bottom, we need: lift > frontHeight * (1 - scale)
|
|
498
|
+
// but we only want a small peek (8-12px visible), so lift = frontHeight - peekAmount
|
|
499
|
+
// simplified: use a fixed peek amount that creates nice visual stacking
|
|
500
|
+
// for bottom position: back toasts need to peek ABOVE front toast
|
|
501
|
+
// with bottom:0 anchor and transformOrigin:bottom, we need lift > toast height to peek
|
|
502
|
+
// use front toast height minus desired overlap as lift
|
|
503
|
+
const peekVisible = 10 // how many pixels of back toast border should peek
|
|
504
|
+
const liftPerToast = peekVisible // lift this much for each toast in stack
|
|
505
|
+
const stackY = expanded
|
|
506
|
+
? baseOffset
|
|
507
|
+
: isFront
|
|
508
|
+
? 0
|
|
509
|
+
: isTop
|
|
510
|
+
? liftPerToast * index // for top position, toasts stack downward
|
|
511
|
+
: -liftPerToast * index // for bottom position, toasts stack upward
|
|
512
|
+
|
|
513
|
+
// final computed values including drag
|
|
514
|
+
const computedY = stackY + dragOffsetY
|
|
515
|
+
const computedX = dragOffsetX
|
|
516
|
+
const computedScale = stackScale
|
|
517
|
+
|
|
518
|
+
// opacity: toasts beyond visibleToasts are always hidden (like Sonner)
|
|
519
|
+
// this applies in BOTH collapsed and expanded states
|
|
520
|
+
// in collapsed mode, the last visible toast also fades slightly
|
|
521
|
+
let computedOpacity = 1
|
|
522
|
+
if (index >= visibleToasts) {
|
|
523
|
+
computedOpacity = 0 // completely hidden beyond limit (both states)
|
|
524
|
+
} else if (!expanded && index === visibleToasts - 1) {
|
|
525
|
+
computedOpacity = 0.5 // last visible toast fades in collapsed mode only
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// z-index: front toast should be on top, back toasts below
|
|
529
|
+
// higher z-index = more in front
|
|
530
|
+
const computedZIndex = visibleToasts - index
|
|
531
|
+
// in collapsed mode, set back toasts to front toast height (sonner pattern)
|
|
532
|
+
// this makes the stack work by having all toasts same height
|
|
533
|
+
const computedHeight = !expanded && !isFront ? frontToastHeight : undefined
|
|
534
|
+
// hidden toasts should not intercept pointer events (like Sonner)
|
|
535
|
+
const computedPointerEvents = index >= visibleToasts ? 'none' : 'auto'
|
|
536
|
+
|
|
537
|
+
// render custom JSX if provided
|
|
538
|
+
if (toast.jsx) {
|
|
539
|
+
return toast.jsx
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
const title = typeof toast.title === 'function' ? toast.title() : toast.title
|
|
543
|
+
const description =
|
|
544
|
+
typeof toast.description === 'function' ? toast.description() : toast.description
|
|
545
|
+
|
|
546
|
+
// data attributes for testing/styling - use dataSet for RN Web compatibility
|
|
547
|
+
const dataSet = {
|
|
548
|
+
mounted: mounted ? 'true' : 'false',
|
|
549
|
+
removed: removed ? 'true' : 'false',
|
|
550
|
+
swipeOut: swipeOut ? 'true' : 'false',
|
|
551
|
+
visible: isVisible ? 'true' : 'false',
|
|
552
|
+
front: isFront ? 'true' : 'false',
|
|
553
|
+
index: String(index),
|
|
554
|
+
type: toastType,
|
|
555
|
+
expanded: expanded ? 'true' : 'false',
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// gap filler height - extends hit area to prevent flicker when moving between toasts
|
|
559
|
+
// only needed when expanded (toasts have visual gaps between them)
|
|
560
|
+
const gapFillerHeight = expanded ? gap + 1 : 0
|
|
561
|
+
|
|
562
|
+
return (
|
|
563
|
+
<ToastItemFrame
|
|
564
|
+
ref={toastRef}
|
|
565
|
+
// biome-ignore lint/a11y/useSemanticElements: we can't use <output> element as this is a styled Tamagui component
|
|
566
|
+
role="status"
|
|
567
|
+
aria-live="polite"
|
|
568
|
+
aria-atomic
|
|
569
|
+
tabIndex={0}
|
|
570
|
+
// @ts-expect-error dataSet is a valid prop for RN Web compatibility
|
|
571
|
+
dataSet={dataSet}
|
|
572
|
+
data-expanded={expanded ? 'true' : 'false'}
|
|
573
|
+
onLayout={handleLayout}
|
|
574
|
+
// use Tamagui animation system - disable animation while dragging
|
|
575
|
+
transition={dragState.isDragging ? undefined : 'quick'}
|
|
576
|
+
// animation props
|
|
577
|
+
y={computedY}
|
|
578
|
+
x={computedX}
|
|
579
|
+
scale={computedScale}
|
|
580
|
+
opacity={computedOpacity}
|
|
581
|
+
zIndex={computedZIndex}
|
|
582
|
+
height={computedHeight}
|
|
583
|
+
overflow={computedHeight ? 'hidden' : undefined}
|
|
584
|
+
pointerEvents={computedPointerEvents as any}
|
|
585
|
+
// anchor position: top positions anchor at top, bottom positions anchor at bottom
|
|
586
|
+
top={isTop ? 0 : undefined}
|
|
587
|
+
bottom={isTop ? undefined : 0}
|
|
588
|
+
// transform-origin: scale from bottom for bottom position, top for top position
|
|
589
|
+
// this ensures the stack peek is visible in the correct direction
|
|
590
|
+
{...(isWeb &&
|
|
591
|
+
!isFront &&
|
|
592
|
+
!expanded && {
|
|
593
|
+
style: { transformOrigin: isTop ? 'top center' : 'bottom center' },
|
|
594
|
+
})}
|
|
595
|
+
// enter/exit styles for AnimatePresence
|
|
596
|
+
// subtle animations - small y shift + opacity fade
|
|
597
|
+
enterStyle={{
|
|
598
|
+
opacity: 0,
|
|
599
|
+
y: isTop ? -10 : 10,
|
|
600
|
+
scale: 0.95,
|
|
601
|
+
}}
|
|
602
|
+
exitStyle={{
|
|
603
|
+
opacity: 0,
|
|
604
|
+
// for swipe dismissal, continue in swipe direction with subtle movement
|
|
605
|
+
x: isHorizontalSwipe && swipeOut ? (swipeDirection === 'left' ? -30 : 30) : 0,
|
|
606
|
+
y:
|
|
607
|
+
isVerticalSwipe && swipeOut
|
|
608
|
+
? swipeDirection === 'up'
|
|
609
|
+
? -30
|
|
610
|
+
: 30
|
|
611
|
+
: isTop
|
|
612
|
+
? -10
|
|
613
|
+
: 10,
|
|
614
|
+
scale: 0.95,
|
|
615
|
+
}}
|
|
616
|
+
{...gestureHandlers}
|
|
617
|
+
{...(isWeb && {
|
|
618
|
+
onKeyDown: (event: React.KeyboardEvent) => {
|
|
619
|
+
if (event.key === 'Escape' && dismissible) {
|
|
620
|
+
handleClose()
|
|
621
|
+
}
|
|
622
|
+
},
|
|
623
|
+
})}
|
|
624
|
+
>
|
|
625
|
+
{/* invisible hit area that fills the gap above/below toast when expanded
|
|
626
|
+
this prevents hover state flickering when mouse moves between toasts */}
|
|
627
|
+
{expanded && gapFillerHeight > 0 && (
|
|
628
|
+
<View
|
|
629
|
+
position="absolute"
|
|
630
|
+
left={0}
|
|
631
|
+
right={0}
|
|
632
|
+
height={gapFillerHeight}
|
|
633
|
+
pointerEvents="auto"
|
|
634
|
+
{
|
|
635
|
+
...(isTop
|
|
636
|
+
? { top: '100%' } // for top position, extend downward
|
|
637
|
+
: { bottom: '100%' }) // for bottom position, extend upward
|
|
638
|
+
}
|
|
639
|
+
/>
|
|
640
|
+
)}
|
|
641
|
+
<XStack alignItems="flex-start" gap="$3">
|
|
642
|
+
{icon && (
|
|
643
|
+
<View flexShrink={0} marginTop="$0.5">
|
|
644
|
+
{icon}
|
|
645
|
+
</View>
|
|
646
|
+
)}
|
|
647
|
+
|
|
648
|
+
<YStack flex={1} gap="$1">
|
|
649
|
+
{title && <ToastItemTitle>{title}</ToastItemTitle>}
|
|
650
|
+
{description && <ToastItemDescription>{description}</ToastItemDescription>}
|
|
651
|
+
</YStack>
|
|
652
|
+
|
|
653
|
+
{closeButton && dismissible && (
|
|
654
|
+
<ToastCloseButton onPress={handleClose} aria-label="Close toast">
|
|
655
|
+
{icons?.close ?? <DefaultCloseIcon />}
|
|
656
|
+
</ToastCloseButton>
|
|
657
|
+
)}
|
|
658
|
+
</XStack>
|
|
659
|
+
|
|
660
|
+
{/* Action buttons */}
|
|
661
|
+
{(toast.action || toast.cancel) && (
|
|
662
|
+
<XStack marginTop="$3" gap="$2" justifyContent="flex-end">
|
|
663
|
+
{toast.cancel && (
|
|
664
|
+
<ToastActionButton
|
|
665
|
+
onPress={(event) => {
|
|
666
|
+
toast.cancel?.onClick?.(event as any)
|
|
667
|
+
handleClose()
|
|
668
|
+
}}
|
|
669
|
+
>
|
|
670
|
+
<SizableText size="$2">{toast.cancel.label}</SizableText>
|
|
671
|
+
</ToastActionButton>
|
|
672
|
+
)}
|
|
673
|
+
{toast.action && (
|
|
674
|
+
<ToastActionButton
|
|
675
|
+
primary
|
|
676
|
+
onPress={(event) => {
|
|
677
|
+
toast.action?.onClick?.(event as any)
|
|
678
|
+
if (!(event as any).defaultPrevented) {
|
|
679
|
+
handleClose()
|
|
680
|
+
}
|
|
681
|
+
}}
|
|
682
|
+
>
|
|
683
|
+
<SizableText size="$2" fontWeight="600" color="$background">
|
|
684
|
+
{toast.action.label}
|
|
685
|
+
</SizableText>
|
|
686
|
+
</ToastActionButton>
|
|
687
|
+
)}
|
|
688
|
+
</XStack>
|
|
689
|
+
)}
|
|
690
|
+
</ToastItemFrame>
|
|
691
|
+
)
|
|
692
|
+
})
|
|
693
|
+
|
|
694
|
+
ToastItem.displayName = 'ToastItem'
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Portal } from '@tamagui/portal'
|
|
2
|
+
import type { ReactNode } from 'react'
|
|
3
|
+
import { Platform } from 'react-native'
|
|
4
|
+
import { ReprogapateToastProvider, type ToastProviderContextValue } from './ToastProvider'
|
|
5
|
+
|
|
6
|
+
export function ToastPortal(props: {
|
|
7
|
+
children: ReactNode
|
|
8
|
+
zIndex?: number
|
|
9
|
+
context: ToastProviderContextValue
|
|
10
|
+
}) {
|
|
11
|
+
const { context, children, zIndex } = props
|
|
12
|
+
let content = children
|
|
13
|
+
if (Platform.OS === 'android' || Platform.OS === 'ios') {
|
|
14
|
+
content = (
|
|
15
|
+
<ReprogapateToastProvider context={context}>{children}</ReprogapateToastProvider>
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
return <Portal zIndex={zIndex || Number.MAX_SAFE_INTEGER}>{content}</Portal>
|
|
19
|
+
}
|