diff --git a/src/components/Toast/Toast.tsx b/src/components/Toast/Toast.tsx index 4d782597da..fba06e3301 100644 --- a/src/components/Toast/Toast.tsx +++ b/src/components/Toast/Toast.tsx @@ -4,9 +4,10 @@ import {View} from 'react-native' import {atoms as a, select, useAlf, useTheme} from '#/alf' import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' import {CircleInfo_Stroke2_Corner0_Rounded as ErrorIcon} from '#/components/icons/CircleInfo' +import {type Props as SVGIconProps} from '#/components/icons/common' import {Warning_Stroke2_Corner0_Rounded as WarningIcon} from '#/components/icons/Warning' import {type ToastType} from '#/components/Toast/types' -import {Text} from '#/components/Typography' +import {Text as BaseText} from '#/components/Typography' import {CircleCheck_Stroke2_Corner0_Rounded as CircleCheck} from '../icons/CircleCheck' type ContextType = { @@ -15,7 +16,7 @@ type ContextType = { export type ToastComponentProps = { type?: ToastType - content: React.ReactNode + content: string } export const ICONS = { @@ -31,20 +32,24 @@ const Context = createContext({ }) Context.displayName = 'ToastContext' -export function Toast({type = 'default', content}: ToastComponentProps) { - const {fonts} = useAlf() +export function Default({type = 'default', content}: ToastComponentProps) { + return ( + + + {content} + + ) +} + +export function Outer({ + children, + type = 'default', +}: { + children: React.ReactNode + type?: ToastType +}) { const t = useTheme() const styles = useToastStyles({type}) - const Icon = ICONS[type] - /** - * Vibes-based number, adjusts `top` of `View` that wraps the text to - * compensate for different type sizes and keep the first line of text - * aligned with the icon. - esb - */ - const fontScaleCompensation = useMemo( - () => parseInt(fonts.scale) * -1 * 0.65, - [fonts.scale], - ) return ( ({type}), [type])}> @@ -64,43 +69,54 @@ export function Toast({type = 'default', content}: ToastComponentProps) { borderColor: styles.borderColor, }, ]}> - - - - {typeof content === 'string' ? ( - {content} - ) : ( - content - )} - + {children} ) } -export function ToastText({children}: {children: React.ReactNode}) { +export function Icon({icon}: {icon?: React.ComponentType}) { + const {type} = useContext(Context) + const styles = useToastStyles({type}) + const IconComponent = icon || ICONS[type] + return +} + +export function Text({children}: {children: React.ReactNode}) { + const {fonts} = useAlf() const {type} = useContext(Context) const {textColor} = useToastStyles({type}) + /** + * Vibes-based number, adjusts `top` of `View` that wraps the text to + * compensate for different type sizes and keep the first line of text + * aligned with the icon. - esb + */ + const fontScaleCompensation = useMemo( + () => parseInt(fonts.scale) * -1 * 0.65, + [fonts.scale], + ) return ( - - {children} - + + {children} + + ) } diff --git a/src/components/Toast/index.tsx b/src/components/Toast/index.tsx index 286d414a14..7a99eebce7 100644 --- a/src/components/Toast/index.tsx +++ b/src/components/Toast/index.tsx @@ -1,15 +1,17 @@ +import React from 'react' import {View} from 'react-native' import {toast as sonner, Toaster} from 'sonner-native' import {atoms as a} from '#/alf' import {DURATION} from '#/components/Toast/const' import { - Toast as BaseToast, + Default as DefaultToast, type ToastComponentProps, } from '#/components/Toast/Toast' import {type BaseToastOptions} from '#/components/Toast/types' export {DURATION} from '#/components/Toast/const' +export {Icon, Outer, Text} from '#/components/Toast/Toast' /** * Toasts are rendered in a global outlet, which is placed at the top of the @@ -22,10 +24,10 @@ export function ToastOutlet() { /** * The toast UI component */ -export function Toast({type, content}: ToastComponentProps) { +export function Default({type, content}: ToastComponentProps) { return ( - + ) } @@ -42,8 +44,15 @@ export function show( content: React.ReactNode, {type, ...options}: BaseToastOptions = {}, ) { - sonner.custom(, { - ...options, - duration: options?.duration ?? DURATION, - }) + if (typeof content === 'string') { + sonner.custom(, { + ...options, + duration: options?.duration ?? DURATION, + }) + } else if (React.isValidElement(content)) { + sonner.custom(content, { + ...options, + duration: options?.duration ?? DURATION, + }) + } } diff --git a/src/components/Toast/index.web.tsx b/src/components/Toast/index.web.tsx index 857ed7b39a..06b33b5834 100644 --- a/src/components/Toast/index.web.tsx +++ b/src/components/Toast/index.web.tsx @@ -1,10 +1,14 @@ +import React from 'react' import {toast as sonner, Toaster} from 'sonner' import {atoms as a} from '#/alf' import {DURATION} from '#/components/Toast/const' -import {Toast} from '#/components/Toast/Toast' +import {Default as DefaultToast} from '#/components/Toast/Toast' import {type BaseToastOptions} from '#/components/Toast/types' +export {DURATION} from '#/components/Toast/const' +export * from '#/components/Toast/Toast' + /** * Toasts are rendered in a global outlet, which is placed at the top of the * component tree. @@ -32,9 +36,17 @@ export function show( content: React.ReactNode, {type, ...options}: BaseToastOptions = {}, ) { - sonner(, { - unstyled: true, // required on web - ...options, - duration: options?.duration ?? DURATION, - }) + if (typeof content === 'string') { + sonner(, { + unstyled: true, // required on web + ...options, + duration: options?.duration ?? DURATION, + }) + } else if (React.isValidElement(content)) { + sonner(content, { + unstyled: true, // required on web + ...options, + duration: options?.duration ?? DURATION, + }) + } } diff --git a/src/view/screens/Storybook/Toasts.tsx b/src/view/screens/Storybook/Toasts.tsx index 98d5b05e32..9d289b9fc0 100644 --- a/src/view/screens/Storybook/Toasts.tsx +++ b/src/view/screens/Storybook/Toasts.tsx @@ -3,7 +3,7 @@ import {Pressable, View} from 'react-native' import {show as deprecatedShow} from '#/view/com/util/Toast' import {atoms as a} from '#/alf' import * as toast from '#/components/Toast' -import {Toast} from '#/components/Toast/Toast' +import {Default as Toast} from '#/components/Toast/Toast' import {H1} from '#/components/Typography' export function Toasts() {