Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,11 @@ toast('Hello World', {
text: TextStyle,
indicator: ViewStyle
},
animationType: 'timing' | 'spring',
animationConfig: {
flingPositionReturnDuration: number,
...(springConfig | timingConfig)
},
});
```

Expand Down Expand Up @@ -402,6 +407,63 @@ Every type has its own duration. You can overwrite them `duration` with the toas

<br />

### Animation Options
You can now control the animation type and configuration for toasts.

#### Props

- **animationType** (`'spring' | 'timing'`, optional): Choose the animation type for toast appearance. By default, toasts positioned at the bottom use spring, and those at the top use timing.
- **animationConfig** (object, optional): Customize the animation configuration for spring or timing.

#### Example Usage

```javascript
import { toast } from 'react-native-toast';

// Show a toast with custom animation settings
toast.show('This is a toast message', {
animationType: 'spring',
animationConfig: {
duration: 500,
stiffness: 100,
},
position: 'top',
});
````

### Global Animation Configuration/Type

You can define a `globalAnimationType` and a `globalAnimationConfig` that sets the default animation configuration for all toasts. If an individual toast specifies its own `animationConfig`, it will override this global setting.

#### Props

- **globalAnimationConfig** (object, optional): Provides a default configuration for toast animations using either spring or timing options.

#### Example Usage

```javascript
import { Toasts } from 'react-native-toast';

// In your component
<Toasts
globalAnimationType="spring"
globalAnimationConfig={{
duration: 500,
stiffness: 120,
}}
/>

// Or when showing a toast
toast.show('This is a toast message', {
position: 'bottom',
animationType: 'spring',
animationConfig: {
duration: 400,
damping: 10,
},
});
```


### Dismiss toast programmatically

Expand Down
59 changes: 40 additions & 19 deletions src/components/Toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@ import {
ViewStyle,
} from 'react-native';
import Animated, {
Easing,
ReduceMotion,
runOnJS,
useAnimatedStyle,
useSharedValue,
withSpring,
type WithSpringConfig,
withTiming,
type WithTimingConfig,
} from 'react-native-reanimated';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import {
Expand Down Expand Up @@ -112,15 +116,33 @@ export const Toast: FC<Props> = ({
}, []);

const setPosition = useCallback(() => {
//control the position of the toast when rendering
//based on offset, visibility, keyboard, and toast height
let timingConfig: WithTimingConfig = { duration: 300 };
let springConfig: WithSpringConfig = { stiffness: 80 };

if (toast.animationConfig) {
const {
duration = 300,
easing = Easing.inOut(Easing.quad),
reduceMotion = ReduceMotion.System,
...spring
} = toast.animationConfig;
timingConfig = { duration, easing, reduceMotion };
springConfig = spring;
}

const useSpringAnimation = toast.animationType === 'spring';

const animation = useSpringAnimation ? withSpring : withTiming;

if (toast.position === ToastPosition.TOP) {
offsetY.value = withTiming(toast.visible ? offset : startingY, {
duration: toast?.animationConfig?.animationDuration ?? 300,
});
position.value = withTiming(toast.visible ? offset : startingY, {
duration: toast?.animationConfig?.animationDuration ?? 300,
});
offsetY.value = animation(
toast.visible ? offset : startingY,
useSpringAnimation ? springConfig : timingConfig
);
position.value = animation(
toast.visible ? offset : startingY,
useSpringAnimation ? springConfig : timingConfig
);
} else {
let kbHeight = keyboardVisible ? keyboardHeight : 0;
const val = toast.visible
Expand All @@ -133,13 +155,14 @@ export const Toast: FC<Props> = ({
24
: startingY;

offsetY.value = withSpring(val, {
stiffness: toast?.animationConfig?.animationStiffness ?? 80,
});

position.value = withSpring(val, {
stiffness: toast?.animationConfig?.animationStiffness ?? 80,
});
offsetY.value = animation(
val,
useSpringAnimation ? springConfig : timingConfig
);
position.value = animation(
val,
useSpringAnimation ? springConfig : timingConfig
);
}
}, [
offset,
Expand All @@ -154,6 +177,7 @@ export const Toast: FC<Props> = ({
offsetY,
extraInsets,
toast.animationConfig,
toast.animationType,
]);

const composedGesture = useMemo(() => {
Expand Down Expand Up @@ -192,21 +216,18 @@ export const Toast: FC<Props> = ({
]);

useEffect(() => {
//set the toast height if it updates while rendered
setToastHeight(toast?.height ? toast.height : DEFAULT_TOAST_HEIGHT);
}, [toast.height]);

useEffect(() => {
//set the toast width if it updates while rendered
setToastWidth(
toast?.width ? toast.width : width - 32 > 360 ? 360 : width - 32
);
}, [toast.width, width]);

useEffect(() => {
//Control visibility of toast when rendering
opacity.value = withTiming(toast.visible ? 1 : 0, {
duration: toast?.animationConfig?.animationDuration ?? 300,
duration: toast?.animationConfig?.duration ?? 300,
});
}, [toast.visible, opacity, toast.animationConfig]);

Expand Down
16 changes: 14 additions & 2 deletions src/components/Toasts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import { TextStyle, View, ViewStyle } from 'react-native';
import { Toast as T, useToaster } from '../headless';
import { Toast } from './Toast';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { ExtraInsets } from '../core/types';
import {
ExtraInsets,
ToastAnimationConfig,
ToastAnimationType,
} from '../core/types';
import { useScreenReader } from 'src/core/utils';

type Props = {
Expand All @@ -21,6 +25,8 @@ type Props = {
text?: TextStyle;
indicator?: ViewStyle;
};
globalAnimationType?: ToastAnimationType;
globalAnimationConfig?: ToastAnimationConfig;
};

export const Toasts: FunctionComponent<Props> = ({
Expand All @@ -32,6 +38,8 @@ export const Toasts: FunctionComponent<Props> = ({
providerKey = 'DEFAULT',
preventScreenReaderFromHiding,
defaultStyle,
globalAnimationType,
globalAnimationConfig,
}) => {
const { toasts, handlers } = useToaster({ providerKey });
const { startPause, endPause } = handlers;
Expand All @@ -56,7 +64,11 @@ export const Toasts: FunctionComponent<Props> = ({
{toasts.map((t) => (
<Toast
key={t.id}
toast={t}
toast={{
...t,
animationType: t.animationType || globalAnimationType,
animationConfig: t.animationConfig || globalAnimationConfig,
}}
startPause={startPause}
endPause={endPause}
updateHeight={handlers.updateHeight}
Expand Down
2 changes: 2 additions & 0 deletions src/core/toast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ const createToast = (
position: ToastPosition.TOP,
providerKey: 'DEFAULT',
isSwipeable: true,
animationType: opts?.animationType ?? 'timing',
animationConfig: opts?.animationConfig,
...opts,
id: opts?.id || genId(),
});
Expand Down
15 changes: 10 additions & 5 deletions src/core/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { TextStyle, ViewStyle } from 'react-native';
import { WithSpringConfig, WithTimingConfig } from 'react-native-reanimated';

export type ToastType = 'success' | 'error' | 'loading' | 'blank';
export enum ToastPosition {
Expand All @@ -12,6 +13,12 @@ export interface IconTheme {
primary: string;
secondary: string;
}
export type ToastAnimationType = 'spring' | 'timing';

export type ToastAnimationConfig = {
flingPositionReturnDuration?: number;
} & WithSpringConfig &
WithTimingConfig;

export type ValueFunction<TValue, TArg> = (arg: TArg) => TValue;
export type ValueOrFunction<TValue, TArg> =
Expand Down Expand Up @@ -51,11 +58,8 @@ export interface Toast {
customToast?: (toast: Toast) => JSX.Element;
providerKey: string;
isSwipeable?: boolean;
animationConfig?: {
flingPositionReturnDuration?: number;
animationStiffness?: number;
animationDuration?: number;
};
animationType?: ToastAnimationType;
animationConfig?: ToastAnimationConfig;
}

export type ToastOptions = Partial<
Expand All @@ -74,6 +78,7 @@ export type ToastOptions = Partial<
| 'providerKey'
| 'isSwipeable'
| 'animationConfig'
| 'animationType'
>
>;

Expand Down
2 changes: 1 addition & 1 deletion src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { useToaster } from './core/use-toaster';
export { Toasts } from './components';
export * from './headless';
export { ToastPosition } from './core/types';
export { ToastPosition, ToastAnimationType } from './core/types';