Skip to content

Commit d757070

Browse files
committed
fix(toast): hadle content overflow issue
1 parent 29c3ebe commit d757070

File tree

8 files changed

+202
-128
lines changed

8 files changed

+202
-128
lines changed

example/src/app/(home)/components/toast.tsx

Lines changed: 132 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
/* eslint-disable react/no-unstable-nested-components */
2+
import Feather from '@expo/vector-icons/Feather';
3+
import Octicons from '@expo/vector-icons/Octicons';
24
import {
35
Button,
46
Toast,
@@ -8,79 +10,142 @@ import {
810
import { useCallback } from 'react';
911
import { View } from 'react-native';
1012
import { toast as sonnerToast } from 'sonner-native';
13+
import { withUniwind } from 'uniwind';
1114
import type { UsageVariant } from '../../../components/component-presentation/types';
1215
import { UsageVariantFlatList } from '../../../components/component-presentation/usage-variant-flatlist';
1316

14-
// const AllVariantsContent = () => {
15-
// return (
16-
// <View className="flex-1 px-5">
17-
// <View className="flex-1 justify-center gap-4">
18-
// {/* Default variant */}
19-
// <Toast variant="default" className="flex-row items-center gap-3">
20-
// <View className="flex-1">
21-
// <Toast.Label>Default notification</Toast.Label>
22-
// <Toast.Description>
23-
// This is a default toast message
24-
// </Toast.Description>
25-
// </View>
26-
// <Toast.Action>Action</Toast.Action>
27-
// </Toast>
17+
const StyledFeather = withUniwind(Feather);
18+
const StyledOcticons = withUniwind(Octicons);
2819

29-
// {/* Accent variant */}
30-
// <Toast variant="accent" className="flex-row items-center gap-3">
31-
// <View className="flex-1">
32-
// <Toast.Label>Accent notification</Toast.Label>
33-
// <Toast.Description>
34-
// This is an accent toast message
35-
// </Toast.Description>
36-
// </View>
37-
// <Toast.Action>Action</Toast.Action>
38-
// </Toast>
39-
40-
// {/* Success variant */}
41-
// <Toast variant="success" className="flex-row items-center gap-3">
42-
// <View className="flex-1">
43-
// <Toast.Label>Success notification</Toast.Label>
44-
// <Toast.Description>
45-
// This is a success toast message
46-
// </Toast.Description>
47-
// </View>
48-
// <Toast.Action>Action</Toast.Action>
49-
// </Toast>
50-
51-
// {/* Warning variant */}
52-
// <Toast variant="warning" className="flex-row items-center gap-3">
53-
// <View className="flex-1">
54-
// <Toast.Label>Warning notification</Toast.Label>
55-
// <Toast.Description>
56-
// This is a warning toast message
57-
// </Toast.Description>
58-
// </View>
59-
// <Toast.Action>Action</Toast.Action>
60-
// </Toast>
20+
const DefaultVariantsContent = () => {
21+
const { toast } = useToast();
6122

62-
// {/* Danger variant */}
63-
// <Toast variant="danger" className="flex-row items-center gap-3">
64-
// <View className="flex-1">
65-
// <Toast.Label>Danger notification</Toast.Label>
66-
// <Toast.Description>
67-
// This is a danger toast message
68-
// </Toast.Description>
69-
// </View>
70-
// <Toast.Action>Action</Toast.Action>
71-
// </Toast>
72-
// </View>
73-
// </View>
74-
// );
75-
// };
23+
return (
24+
<View className="flex-1 items-center justify-center px-5 gap-5">
25+
<Button
26+
variant="secondary"
27+
onPress={() =>
28+
toast.show({
29+
variant: 'default',
30+
label: 'Join a team',
31+
description: 'Junior sent you an invitation to join HeroUI team',
32+
icon: (
33+
<StyledFeather
34+
name="users"
35+
size={15}
36+
className="text-foreground mt-[3px]"
37+
/>
38+
),
39+
actionLabel: 'Close',
40+
onActionPress: ({ hide }) => hide(),
41+
})
42+
}
43+
>
44+
Default toast
45+
</Button>
46+
<Button
47+
variant="secondary"
48+
onPress={() =>
49+
toast.show({
50+
variant: 'accent',
51+
label: 'You have 2 credits left',
52+
description: 'Get a paid plan for more credits',
53+
icon: (
54+
<StyledFeather
55+
name="info"
56+
size={18}
57+
className="text-accent mt-0.5"
58+
/>
59+
),
60+
actionLabel: 'Close',
61+
onActionPress: ({ hide }) => hide(),
62+
})
63+
}
64+
>
65+
Accent toast
66+
</Button>
67+
<Button
68+
variant="secondary"
69+
onPress={() =>
70+
toast.show({
71+
variant: 'success',
72+
label: 'You have upgraded your plan',
73+
description: 'You can continue using HeroUI Chat',
74+
icon: (
75+
<StyledOcticons
76+
name="shield-check"
77+
size={18}
78+
className="text-success mt-0.5"
79+
/>
80+
),
81+
actionLabel: 'Close',
82+
onActionPress: ({ hide }) => hide(),
83+
})
84+
}
85+
>
86+
Success toast
87+
</Button>
88+
<Button
89+
variant="secondary"
90+
onPress={() =>
91+
toast.show({
92+
variant: 'warning',
93+
label: 'You have no credits left',
94+
description: 'Upgrade to a paid plan to continue',
95+
icon: (
96+
<StyledOcticons
97+
name="shield"
98+
size={18}
99+
className="text-warning mt-0.5"
100+
/>
101+
),
102+
actionLabel: 'Close',
103+
onActionPress: ({ hide }) => hide(),
104+
})
105+
}
106+
>
107+
Warning toast
108+
</Button>
109+
<Button
110+
variant="secondary"
111+
onPress={() =>
112+
toast.show({
113+
variant: 'danger',
114+
label: 'Storage is full',
115+
description:
116+
"Remove files to release space. I'm adding more text as usual but it's okay I guess I just want to see how it looks with a lot of information",
117+
icon: (
118+
<StyledFeather
119+
name="hard-drive"
120+
size={16}
121+
className="text-danger mt-[3px]"
122+
/>
123+
),
124+
actionLabel: 'Close',
125+
onActionPress: ({ hide }) => hide(),
126+
})
127+
}
128+
>
129+
Danger toast
130+
</Button>
131+
</View>
132+
);
133+
};
76134

77135
// ------------------------------------------------------------------------------
78136

79137
const MyToast1 = (props: ToastComponentProps) => {
80138
const { id, hide } = props;
81139

82140
return (
83-
<Toast variant="success" className="flex-row items-center gap-3" {...props}>
141+
<Toast
142+
variant="success"
143+
className="flex-row items-center gap-3"
144+
classNames={{
145+
overlay: 'bg-green-500',
146+
}}
147+
{...props}
148+
>
84149
<View className="flex-1">
85150
<Toast.Label>{id}</Toast.Label>
86151
<Toast.Description>
@@ -151,7 +216,7 @@ const InteractiveDemoContent = () => {
151216
<Button
152217
onPress={() =>
153218
toast.show({
154-
duration: 5000,
219+
duration: 'persistent',
155220
component: _renderToast1,
156221
})
157222
}
@@ -294,6 +359,11 @@ const MultipleToastsContent = () => {
294359
// ------------------------------------------------------------------------------
295360

296361
const TOAST_VARIANTS: UsageVariant[] = [
362+
{
363+
value: 'default-variants',
364+
label: 'Default Variants',
365+
content: <DefaultVariantsContent />,
366+
},
297367
{
298368
value: 'interactive-demo',
299369
label: 'Interactive Demo',

src/components/toast/toast.animation.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,27 @@ export function useToastRootAnimation(options: UseToastRootAnimationOptions) {
360360
};
361361
});
362362

363+
const rOverlayStyle = useAnimatedStyle(() => {
364+
const totalValue = total.get();
365+
const isLastToast = index === totalValue - 1;
366+
367+
if (isAnimationDisabled) {
368+
return {
369+
opacity: isLastToast ? 0 : 1,
370+
};
371+
}
372+
373+
const opacity = interpolate(
374+
index,
375+
[totalValue - 1, totalValue - 2],
376+
[0, 1]
377+
);
378+
379+
return {
380+
opacity: withTiming(opacity, { duration: 150 }),
381+
};
382+
});
383+
363384
// Determine entering and exiting animations based on placement
364385
const enteringAnimation =
365386
placement === 'top' ? enteringTopValue : enteringBottomValue;
@@ -368,6 +389,7 @@ export function useToastRootAnimation(options: UseToastRootAnimationOptions) {
368389

369390
return {
370391
rContainerStyle,
392+
rOverlayStyle,
371393
isAnimationDisabled,
372394
entering: isAnimationDisabled ? undefined : enteringAnimation,
373395
exiting: isAnimationDisabled ? undefined : exitingAnimation,

src/components/toast/toast.hooks.ts

Lines changed: 0 additions & 59 deletions
This file was deleted.

src/components/toast/toast.styles.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,15 @@ import { StyleSheet } from 'react-native';
22
import { tv } from 'tailwind-variants';
33
import { combineStyles } from '../../helpers/theme/utils/combine-styles';
44

5+
/**
6+
* Toast root styles with container and overlay slots
7+
*/
58
const root = tv({
6-
base: 'rounded-3xl p-4 bg-surface border border-muted/10 shadow-2xl shadow-black/5 overflow-hidden',
9+
slots: {
10+
container:
11+
'rounded-3xl px-3 py-4 bg-surface border border-muted/10 shadow-2xl shadow-black/5 overflow-hidden',
12+
overlay: 'absolute -top-1 left-0 right-0 -bottom-1 bg-surface rounded-3xl',
13+
},
714
});
815

916
const label = tv({
@@ -49,6 +56,11 @@ const toastStyles = combineStyles({
4956
action,
5057
});
5158

59+
/**
60+
* Export slot types for type-safe classNames props
61+
*/
62+
export type ToastRootSlots = keyof ReturnType<typeof root>;
63+
5264
export const styleSheet = StyleSheet.create({
5365
root: {
5466
borderCurve: 'continuous',

0 commit comments

Comments
 (0)