Skip to content

Commit 8f3e9af

Browse files
committed
feat(toast): add maxVisibleToasts prop
1 parent 471c19f commit 8f3e9af

File tree

6 files changed

+58
-18
lines changed

6 files changed

+58
-18
lines changed

src/components/toast/toast.animation.ts

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useWindowDimensions, type ViewStyle } from 'react-native';
1+
import { useWindowDimensions } from 'react-native';
22
import { Gesture } from 'react-native-gesture-handler';
33
import {
44
Easing,
@@ -12,7 +12,6 @@ import {
1212
withDecay,
1313
withSpring,
1414
withTiming,
15-
type SharedValue,
1615
} from 'react-native-reanimated';
1716
import { scheduleOnRN } from 'react-native-worklets';
1817
import {
@@ -22,7 +21,7 @@ import {
2221
getStyleProperties,
2322
getStyleTransform,
2423
} from '../../helpers/utils/animation';
25-
import type { ToastPlacement, ToastRootAnimation } from './toast.types';
24+
import type { UseToastRootAnimationOptions } from './toast.types';
2625

2726
// --------------------------------------------------
2827

@@ -71,17 +70,7 @@ export const exitingBottom = new Keyframe({
7170
* Handles opacity, translateY, and scale animations based on toast index and placement
7271
* Also handles gesture-based swipe to dismiss and rubber-band drag effects
7372
*/
74-
export function useToastRootAnimation(options: {
75-
animation: ToastRootAnimation | undefined;
76-
style: ViewStyle | undefined;
77-
index: number;
78-
total: SharedValue<number>;
79-
heights: SharedValue<Record<string, number>>;
80-
placement: ToastPlacement;
81-
hide?: ((ids?: string | string[]) => void) | undefined;
82-
id?: string | undefined;
83-
isSwipable?: boolean;
84-
}) {
73+
export function useToastRootAnimation(options: UseToastRootAnimationOptions) {
8574
const {
8675
animation,
8776
style,
@@ -92,6 +81,7 @@ export function useToastRootAnimation(options: {
9281
hide,
9382
id,
9483
isSwipable = true,
84+
maxVisibleToasts = 3,
9585
} = options;
9686

9787
const { height: screenHeight } = useWindowDimensions();
@@ -240,7 +230,7 @@ export function useToastRootAnimation(options: {
240230

241231
if (placement === 'top') {
242232
// Top placement: dismiss if swiped up (negative Y)
243-
if (shouldDismiss && translationY < 0 && id && hide) {
233+
if (shouldDismiss && translationY < 0 && id) {
244234
// Use withDecay to continue motion with velocity
245235
gestureTranslateY.set(
246236
withDecay(
@@ -265,7 +255,7 @@ export function useToastRootAnimation(options: {
265255
}
266256
} else {
267257
// Bottom placement: dismiss if swiped down (positive Y)
268-
if (shouldDismiss && translationY > 0 && id && hide) {
258+
if (shouldDismiss && translationY > 0 && id) {
269259
// Use withDecay to continue motion with velocity
270260
gestureTranslateY.set(
271261
withDecay(
@@ -304,7 +294,10 @@ export function useToastRootAnimation(options: {
304294
const totalValue = total.get();
305295

306296
const inputRange = [totalValue - 1, totalValue - 2];
307-
const opacityInputRange = [totalValue - 3, totalValue - 4];
297+
const opacityInputRange = [
298+
totalValue - maxVisibleToasts,
299+
totalValue - maxVisibleToasts - 1,
300+
];
308301

309302
const opacity = interpolate(index, opacityInputRange, opacityValue);
310303
const scale = interpolate(index, inputRange, scaleValue);

src/components/toast/toast.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const ToastRoot = forwardRef<ViewRef, ToastRootProps>((props, ref) => {
4040
index,
4141
total,
4242
heights,
43+
maxVisibleToasts,
4344
className,
4445
style,
4546
animation,
@@ -70,6 +71,7 @@ const ToastRoot = forwardRef<ViewRef, ToastRootProps>((props, ref) => {
7071
hide,
7172
id,
7273
isSwipable,
74+
maxVisibleToasts,
7375
});
7476

7577
const contextValue = useMemo(

src/components/toast/toast.types.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { ViewStyle } from 'react-native';
12
import type {
23
EntryOrExitLayoutType,
34
WithTimingConfig,
@@ -205,3 +206,22 @@ export interface ToastContextValue {
205206
* Ref type for the Toast.Root component
206207
*/
207208
export type ToastRootRef = ViewRef;
209+
210+
/**
211+
* Props for useToastRootAnimation hook
212+
* Picks required properties from ToastRootProps and adds id from ToastComponentProps
213+
*/
214+
export type UseToastRootAnimationOptions = Pick<
215+
ToastRootProps,
216+
| 'animation'
217+
| 'index'
218+
| 'total'
219+
| 'heights'
220+
| 'placement'
221+
| 'hide'
222+
| 'isSwipable'
223+
| 'maxVisibleToasts'
224+
> &
225+
Pick<ToastComponentProps, 'id'> & {
226+
style: ViewStyle | undefined;
227+
};

src/providers/toast/provider.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ const ToasterContext = createContext<ToasterContextValue | null>(null);
2626
* Toast provider component
2727
* Wraps your app to enable toast functionality
2828
*/
29-
export function ToastProvider({ insets, children }: ToastProviderProps) {
29+
export function ToastProvider({
30+
insets,
31+
maxVisibleToasts = 3,
32+
children,
33+
}: ToastProviderProps) {
3034
const [toasts, dispatch] = useReducer(toastReducer, []);
3135

3236
const isToastVisible = toasts.length > 0;
@@ -118,6 +122,7 @@ export function ToastProvider({ insets, children }: ToastProviderProps) {
118122
index={index}
119123
total={total}
120124
heights={heights}
125+
maxVisibleToasts={maxVisibleToasts}
121126
/>
122127
))}
123128
</View>

src/providers/toast/toast-item-renderer.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export const ToastItemRenderer = memo(
1313
index,
1414
total,
1515
heights,
16+
maxVisibleToasts,
1617
}: ToastItemRendererProps) => {
1718
if (typeof toastItem.component !== 'function') {
1819
throw new Error(
@@ -25,6 +26,7 @@ export const ToastItemRenderer = memo(
2526
index,
2627
total,
2728
heights,
29+
maxVisibleToasts,
2830
show,
2931
hide,
3032
});

src/providers/toast/types.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ export interface ToastProviderProps {
3737
* - Android: { top: 12, bottom: 12, left: 12, right: 12 }
3838
*/
3939
insets?: ToastInsets;
40+
/**
41+
* Maximum number of visible toasts before opacity starts fading
42+
* Controls when toast items begin to fade out as they move beyond the visible stack
43+
* @default 3
44+
*/
45+
maxVisibleToasts?: number;
4046
/**
4147
* Children to render
4248
*/
@@ -63,6 +69,12 @@ export interface ToastComponentProps {
6369
* Heights of all toast items, keyed by toast ID
6470
*/
6571
heights: SharedValue<Record<string, number>>;
72+
/**
73+
* Maximum number of visible toasts before opacity starts fading
74+
* Controls when toast items begin to fade out as they move beyond the visible stack
75+
* @default 3
76+
*/
77+
maxVisibleToasts?: number;
6678
/**
6779
* Show a new toast
6880
*/
@@ -172,6 +184,12 @@ export interface ToastItemRendererProps {
172184
* Heights of all toast items, keyed by toast ID
173185
*/
174186
heights: SharedValue<Record<string, number>>;
187+
/**
188+
* Maximum number of visible toasts before opacity starts fading
189+
* Controls when toast items begin to fade out as they move beyond the visible stack
190+
* @default 3
191+
*/
192+
maxVisibleToasts?: number;
175193
/**
176194
* Show a new toast
177195
*/

0 commit comments

Comments
 (0)