forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import React from 'react'
2import {nanoid} from 'nanoid/non-secure'
3import {toast as sonner, Toaster} from 'sonner'
4
5import {atoms as a} from '#/alf'
6import {DURATION} from '#/components/Toast/const'
7import {
8 Icon as ToastIcon,
9 Outer as ToastOuter,
10 Text as ToastText,
11 ToastConfigProvider,
12} from '#/components/Toast/Toast'
13import {type BaseToastOptions} from '#/components/Toast/types'
14
15export {DURATION} from '#/components/Toast/const'
16export * from '#/components/Toast/Toast'
17export {type ToastType} from '#/components/Toast/types'
18
19/**
20 * Toasts are rendered in a global outlet, which is placed at the top of the
21 * component tree.
22 */
23export function ToastOutlet() {
24 return (
25 <Toaster
26 position="bottom-left"
27 gap={a.gap_sm.gap}
28 offset={a.p_xl.padding}
29 mobileOffset={a.p_xl.padding}
30 />
31 )
32}
33
34/**
35 * Access the full Sonner API
36 */
37export const api = sonner
38
39/**
40 * Our base toast API, using the `Toast` export of this file.
41 */
42export function show(
43 content: React.ReactNode,
44 {type = 'default', ...options}: BaseToastOptions = {},
45) {
46 const id = nanoid()
47
48 if (typeof content === 'string') {
49 sonner(
50 <ToastConfigProvider id={id} type={type}>
51 <ToastOuter>
52 <ToastIcon />
53 <ToastText>{content}</ToastText>
54 </ToastOuter>
55 </ToastConfigProvider>,
56 {
57 ...options,
58 unstyled: true, // required on web
59 id,
60 duration: options?.duration ?? DURATION,
61 },
62 )
63 } else if (React.isValidElement(content)) {
64 sonner(
65 <ToastConfigProvider id={id} type={type}>
66 {content}
67 </ToastConfigProvider>,
68 {
69 ...options,
70 unstyled: true, // required on web
71 id,
72 duration: options?.duration ?? DURATION,
73 },
74 )
75 } else {
76 throw new Error(
77 `Toast can be a string or a React element, got ${typeof content}`,
78 )
79 }
80}