Skip to content

Commit 81a3ef7

Browse files
committed
feat(toast): add loading toast example
1 parent c550020 commit 81a3ef7

File tree

2 files changed

+203
-228
lines changed

2 files changed

+203
-228
lines changed

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

Lines changed: 72 additions & 228 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
1-
/* eslint-disable react/no-unstable-nested-components */
21
import Feather from '@expo/vector-icons/Feather';
32
import Octicons from '@expo/vector-icons/Octicons';
4-
import {
5-
Button,
6-
Toast,
7-
useToast,
8-
type ToastComponentProps,
9-
} from 'heroui-native';
3+
import { Button, useToast, type ToastComponentProps } from 'heroui-native';
104
import { useCallback } from 'react';
115
import { View } from 'react-native';
12-
import { toast as sonnerToast } from 'sonner-native';
136
import { withUniwind } from 'uniwind';
147
import type { UsageVariant } from '../../../components/component-presentation/types';
158
import { UsageVariantFlatList } from '../../../components/component-presentation/usage-variant-flatlist';
9+
import {
10+
LoadingToast,
11+
useLoadingState,
12+
} from '../../../components/toast/loading-toast';
1613

1714
const StyledFeather = withUniwind(Feather);
1815
const StyledOcticons = withUniwind(Octicons);
1916

17+
// ------------------------------------------------------------------------------
18+
2019
const DefaultVariantsContent = () => {
2120
const { toast } = useToast();
2221

@@ -128,6 +127,9 @@ const DefaultVariantsContent = () => {
128127
>
129128
Danger toast
130129
</Button>
130+
<Button onPress={() => toast.hide('all')} variant="destructive-soft">
131+
Hide all toasts
132+
</Button>
131133
</View>
132134
);
133135
};
@@ -183,230 +185,77 @@ const DifferentContentSizesContent = () => {
183185
>
184186
Large toast
185187
</Button>
188+
<Button onPress={() => toast.hide('all')} variant="destructive-soft">
189+
Hide all toasts
190+
</Button>
186191
</View>
187192
);
188193
};
189194

190195
// ------------------------------------------------------------------------------
191196

192-
const MyToast1 = (props: ToastComponentProps) => {
193-
const { id, hide } = props;
194-
195-
return (
196-
<Toast
197-
variant="success"
198-
className="flex-row items-center gap-3"
199-
classNames={{
200-
overlay: 'bg-green-500',
201-
}}
202-
{...props}
203-
>
204-
<View className="flex-1">
205-
<Toast.Label>{id}</Toast.Label>
206-
<Toast.Description>
207-
Use buttons below to control this toast
208-
</Toast.Description>
209-
</View>
210-
<Toast.Action onPress={() => hide(id)}>Close</Toast.Action>
211-
</Toast>
212-
);
213-
};
214-
215-
const MyToast2 = (props: ToastComponentProps) => {
216-
const { id, hide } = props;
217-
218-
return (
219-
<Toast variant="success" className="flex-row items-center gap-3" {...props}>
220-
<View className="flex-1">
221-
<Toast.Label>{id}</Toast.Label>
222-
<Toast.Description>
223-
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Nam tenetur
224-
maxime ab laboriosam qui praesentium facere? Ad dolor fugiat,
225-
molestiae esse laudantium sed ut ullam.
226-
</Toast.Description>
227-
</View>
228-
<Toast.Action onPress={() => hide()}>Close</Toast.Action>
229-
</Toast>
230-
);
231-
};
232-
233-
const MyToast3 = (props: ToastComponentProps) => {
234-
const { id, hide } = props;
235-
236-
return (
237-
<Toast variant="warning" className="flex-row items-center gap-3" {...props}>
238-
<View className="flex-1">
239-
<Toast.Label>{id}</Toast.Label>
240-
<Toast.Description>
241-
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Nam tenetur
242-
maxime ab laboriosam qui praesentium facere? Ad dolor fugiat,
243-
molestiae esse laudantium sed ut ullam. Lorem, ipsum dolor sit amet
244-
consectetur adipisicing elit. Nam tenetur maxime ab laboriosam qui
245-
praesentium facere?
246-
</Toast.Description>
247-
</View>
248-
<Toast.Action onPress={() => hide(id)}>Close</Toast.Action>
249-
</Toast>
250-
);
251-
};
252-
const InteractiveDemoContent = () => {
197+
const CustomToastsContent = () => {
253198
const { toast } = useToast();
199+
const TOAST_ID = 'loading-toast';
200+
const { isLoading, setIsLoading } = useLoadingState();
254201

255-
const _renderToast1 = useCallback(
256-
(props: ToastComponentProps) => <MyToast1 {...props} />,
257-
[]
258-
);
259-
const _renderToast2 = useCallback(
260-
(props: ToastComponentProps) => <MyToast2 {...props} />,
261-
[]
262-
);
263-
const _renderToast3 = useCallback(
264-
(props: ToastComponentProps) => <MyToast3 {...props} />,
265-
[]
266-
);
202+
/**
203+
* Simulates loading data (e.g., API call, file upload, etc.)
204+
* In a real app, this would be an actual async operation
205+
*/
206+
const loadData = async (): Promise<void> => {
207+
/**
208+
* Simulate network delay or processing time
209+
*/
210+
await new Promise((resolve) => setTimeout(resolve, 2000));
211+
};
267212

268-
return (
269-
<View className="flex-1 px-5">
270-
<View className="flex-1 justify-center gap-3">
271-
<Button
272-
onPress={() =>
273-
toast.show({
274-
duration: 'persistent',
275-
component: _renderToast1,
276-
})
277-
}
278-
variant="primary"
279-
>
280-
Show Toast 1
281-
</Button>
282-
283-
<Button
284-
onPress={() => {
285-
toast.show({
286-
duration: 5000,
287-
component: _renderToast2,
288-
});
289-
}}
290-
variant="primary"
291-
>
292-
Show Toast 2
293-
</Button>
294-
295-
<Button
296-
onPress={() => {
297-
toast.show({
298-
duration: 5000,
299-
component: _renderToast3,
300-
});
301-
}}
302-
variant="primary"
303-
>
304-
Show Toast 3
305-
</Button>
213+
const renderLoadingToast = useCallback((props: ToastComponentProps) => {
214+
return <LoadingToast {...props} />;
215+
}, []);
306216

307-
<Button onPress={() => toast.hide()} variant="destructive">
308-
Hide Last Toast
309-
</Button>
217+
const handleShowLoadingToast = async () => {
218+
/**
219+
* Set loading to true and show toast
220+
*/
221+
setIsLoading(true);
222+
toast.show({
223+
id: TOAST_ID,
224+
duration: 'persistent',
225+
component: renderLoadingToast,
226+
});
310227

311-
<Button onPress={() => toast.hide('all')} variant="destructive">
312-
Hide All Toasts
313-
</Button>
314-
315-
<Button onPress={() => sonnerToast('Hello, World!')}>
316-
Sonner Toast
317-
</Button>
318-
</View>
319-
</View>
320-
);
321-
};
322-
323-
// ------------------------------------------------------------------------------
324-
325-
const MultipleToastsContent = () => {
326-
const { toast } = useToast();
228+
try {
229+
/**
230+
* Perform the actual async operation
231+
*/
232+
await loadData();
233+
} catch (error) {
234+
/**
235+
* Handle errors if needed
236+
*/
237+
console.error('Failed to load data:', error);
238+
} finally {
239+
/**
240+
* Set loading to false when operation completes
241+
*/
242+
setIsLoading(false);
243+
}
244+
};
327245

328246
return (
329-
<View className="flex-1 px-5">
330-
<View className="flex-1 justify-center gap-3">
331-
<Button
332-
onPress={() => {
333-
// Show multiple toasts with custom IDs
334-
toast.show({
335-
id: 'toast-1',
336-
component: (props: ToastComponentProps) => (
337-
<Toast
338-
variant="default"
339-
placement="top"
340-
className="flex-row items-center gap-3"
341-
{...props}
342-
>
343-
<View className="flex-1">
344-
<Toast.Label>Toast 1</Toast.Label>
345-
<Toast.Description>First toast at top</Toast.Description>
346-
</View>
347-
<Toast.Action onPress={() => props.hide(props.id)}>
348-
Close
349-
</Toast.Action>
350-
</Toast>
351-
),
352-
});
353-
354-
toast.show({
355-
id: 'toast-2',
356-
component: (props: ToastComponentProps) => (
357-
<Toast
358-
variant="accent"
359-
placement="top"
360-
className="flex-row items-center gap-3"
361-
{...props}
362-
>
363-
<View className="flex-1">
364-
<Toast.Label>Toast 2</Toast.Label>
365-
<Toast.Description>Second toast at top</Toast.Description>
366-
</View>
367-
<Toast.Action onPress={() => props.hide(props.id)}>
368-
Close
369-
</Toast.Action>
370-
</Toast>
371-
),
372-
});
373-
374-
toast.show({
375-
id: 'toast-3',
376-
component: (props: ToastComponentProps) => (
377-
<Toast
378-
variant="success"
379-
placement="bottom"
380-
className="flex-row items-center gap-3"
381-
{...props}
382-
>
383-
<View className="flex-1">
384-
<Toast.Label>Toast 3</Toast.Label>
385-
<Toast.Description>Third toast at bottom</Toast.Description>
386-
</View>
387-
<Toast.Action onPress={() => props.hide(props.id)}>
388-
Close
389-
</Toast.Action>
390-
</Toast>
391-
),
392-
});
393-
}}
394-
variant="primary"
395-
>
396-
Show All Toasts
397-
</Button>
398-
399-
<Button
400-
onPress={() => toast.hide(['toast-1', 'toast-2', 'toast-3'])}
401-
variant="secondary"
402-
>
403-
Hide Specific Toasts
404-
</Button>
247+
<View className="flex-1 items-center justify-center px-5 gap-5">
248+
<Button
249+
variant="secondary"
250+
onPress={handleShowLoadingToast}
251+
isDisabled={isLoading}
252+
>
253+
{isLoading ? 'Loading...' : 'Load data'}
254+
</Button>
405255

406-
<Button onPress={() => toast.hide()} variant="destructive">
407-
Hide All Toasts
408-
</Button>
409-
</View>
256+
<Button onPress={() => toast.hide('all')} variant="destructive-soft">
257+
Hide all toasts
258+
</Button>
410259
</View>
411260
);
412261
};
@@ -416,23 +265,18 @@ const MultipleToastsContent = () => {
416265
const TOAST_VARIANTS: UsageVariant[] = [
417266
{
418267
value: 'default-variants',
419-
label: 'Default Variants',
268+
label: 'Default variants',
420269
content: <DefaultVariantsContent />,
421270
},
422271
{
423272
value: 'different-content-sizes',
424-
label: 'Different Content Sizes',
273+
label: 'Different content sizes',
425274
content: <DifferentContentSizesContent />,
426275
},
427276
{
428-
value: 'interactive-demo',
429-
label: 'Interactive Demo',
430-
content: <InteractiveDemoContent />,
431-
},
432-
{
433-
value: 'multiple-toasts',
434-
label: 'Multiple Toasts',
435-
content: <MultipleToastsContent />,
277+
value: 'custom-toasts',
278+
label: 'Custom toasts',
279+
content: <CustomToastsContent />,
436280
},
437281
];
438282

0 commit comments

Comments
 (0)