diff options
-rw-r--r-- | src/components/Toast/Toast.tsx | 91 | ||||
-rw-r--r-- | src/components/Toast/index.e2e.tsx | 8 | ||||
-rw-r--r-- | src/components/Toast/index.tsx | 40 | ||||
-rw-r--r-- | src/components/Toast/index.web.tsx | 30 | ||||
-rw-r--r-- | src/view/com/composer/Composer.tsx | 9 | ||||
-rw-r--r-- | src/view/screens/Storybook/Toasts.tsx | 80 |
6 files changed, 123 insertions, 135 deletions
diff --git a/src/components/Toast/Toast.tsx b/src/components/Toast/Toast.tsx index 53d5e5115..ac5bc4889 100644 --- a/src/components/Toast/Toast.tsx +++ b/src/components/Toast/Toast.tsx @@ -16,19 +16,6 @@ import {dismiss} from '#/components/Toast/sonner' import {type ToastType} from '#/components/Toast/types' import {Text as BaseText} from '#/components/Typography' -type ToastConfigContextType = { - id: string -} - -type ToastThemeContextType = { - type: ToastType -} - -export type ToastComponentProps = { - type?: ToastType - content: string -} - export const ICONS = { default: CircleCheck, success: CircleCheck, @@ -37,81 +24,67 @@ export const ICONS = { info: CircleInfo, } -const ToastConfigContext = createContext<ToastConfigContextType>({ +const ToastConfigContext = createContext<{ + id: string + type: ToastType +}>({ id: '', + type: 'default', }) ToastConfigContext.displayName = 'ToastConfigContext' export function ToastConfigProvider({ children, id, + type, }: { children: React.ReactNode id: string + type: ToastType }) { return ( - <ToastConfigContext.Provider value={useMemo(() => ({id}), [id])}> + <ToastConfigContext.Provider + value={useMemo(() => ({id, type}), [id, type])}> {children} </ToastConfigContext.Provider> ) } -const ToastThemeContext = createContext<ToastThemeContextType>({ - type: 'default', -}) -ToastThemeContext.displayName = 'ToastThemeContext' - -export function Default({type = 'default', content}: ToastComponentProps) { - return ( - <Outer type={type}> - <Icon /> - <Text>{content}</Text> - </Outer> - ) -} - -export function Outer({ - children, - type = 'default', -}: { - children: React.ReactNode - type?: ToastType -}) { +export function Outer({children}: {children: React.ReactNode}) { const t = useTheme() + const {type} = useContext(ToastConfigContext) const styles = useToastStyles({type}) return ( - <ToastThemeContext.Provider value={useMemo(() => ({type}), [type])}> - <View - style={[ - a.flex_1, - a.p_lg, - a.rounded_md, - a.border, - a.flex_row, - a.gap_sm, - t.atoms.shadow_sm, - { - paddingVertical: 14, // 16 seems too big - backgroundColor: styles.backgroundColor, - borderColor: styles.borderColor, - }, - ]}> - {children} - </View> - </ToastThemeContext.Provider> + <View + style={[ + a.flex_1, + a.p_lg, + a.rounded_md, + a.border, + a.flex_row, + a.gap_sm, + t.atoms.shadow_sm, + { + paddingVertical: 14, // 16 seems too big + backgroundColor: styles.backgroundColor, + borderColor: styles.borderColor, + }, + ]}> + {children} + </View> ) } export function Icon({icon}: {icon?: React.ComponentType<SVGIconProps>}) { - const {type} = useContext(ToastThemeContext) + const {type} = useContext(ToastConfigContext) const styles = useToastStyles({type}) const IconComponent = icon || ICONS[type] return <IconComponent size="md" fill={styles.iconColor} /> } export function Text({children}: {children: React.ReactNode}) { - const {type} = useContext(ToastThemeContext) + const {type} = useContext(ToastConfigContext) const {textColor} = useToastStyles({type}) const {fontScaleCompensation} = useToastFontScaleCompensation() return ( @@ -142,12 +115,12 @@ export function Text({children}: {children: React.ReactNode}) { export function Action( props: Omit<ButtonProps, UninheritableButtonProps | 'children'> & { - children: string + children: React.ReactNode }, ) { const t = useTheme() const {fontScaleCompensation} = useToastFontScaleCompensation() - const {type} = useContext(ToastThemeContext) + const {type} = useContext(ToastConfigContext) const {id} = useContext(ToastConfigContext) const styles = useMemo(() => { const base = { diff --git a/src/components/Toast/index.e2e.tsx b/src/components/Toast/index.e2e.tsx index 357bd8dda..a4056323d 100644 --- a/src/components/Toast/index.e2e.tsx +++ b/src/components/Toast/index.e2e.tsx @@ -1,3 +1,11 @@ +export const DURATION = 0 + +export const Action = () => null +export const Icon = () => null +export const Outer = () => null +export const Text = () => null +export const ToastConfigProvider = () => null + export function ToastOutlet() { return null } diff --git a/src/components/Toast/index.tsx b/src/components/Toast/index.tsx index 0d1c661d2..d70a8ad16 100644 --- a/src/components/Toast/index.tsx +++ b/src/components/Toast/index.tsx @@ -6,15 +6,15 @@ import {toast as sonner, Toaster} from 'sonner-native' import {atoms as a} from '#/alf' import {DURATION} from '#/components/Toast/const' import { - Default as DefaultToast, + Icon as ToastIcon, Outer as BaseOuter, - type ToastComponentProps, + Text as ToastText, ToastConfigProvider, } from '#/components/Toast/Toast' import {type BaseToastOptions} from '#/components/Toast/types' export {DURATION} from '#/components/Toast/const' -export {Action, Icon, Text} from '#/components/Toast/Toast' +export {Action, Icon, Text, ToastConfigProvider} from '#/components/Toast/Toast' export {type ToastType} from '#/components/Toast/types' /** @@ -25,27 +25,10 @@ export function ToastOutlet() { return <Toaster pauseWhenPageIsHidden gap={a.gap_sm.gap} /> } -/** - * The toast UI component - */ -export function Default({type, content}: ToastComponentProps) { +export function Outer({children}: {children: React.ReactNode}) { return ( <View style={[a.px_xl, a.w_full]}> - <DefaultToast content={content} type={type} /> - </View> - ) -} - -export function Outer({ - children, - type = 'default', -}: { - children: React.ReactNode - type?: ToastComponentProps['type'] -}) { - return ( - <View style={[a.px_xl, a.w_full]}> - <BaseOuter type={type}>{children}</BaseOuter> + <BaseOuter>{children}</BaseOuter> </View> ) } @@ -60,14 +43,17 @@ export const api = sonner */ export function show( content: React.ReactNode, - {type, ...options}: BaseToastOptions = {}, + {type = 'default', ...options}: BaseToastOptions = {}, ) { const id = nanoid() if (typeof content === 'string') { sonner.custom( - <ToastConfigProvider id={id}> - <Default content={content} type={type} /> + <ToastConfigProvider id={id} type={type}> + <Outer> + <ToastIcon /> + <ToastText>{content}</ToastText> + </Outer> </ToastConfigProvider>, { ...options, @@ -77,7 +63,9 @@ export function show( ) } else if (React.isValidElement(content)) { sonner.custom( - <ToastConfigProvider id={id}>{content}</ToastConfigProvider>, + <ToastConfigProvider id={id} type={type}> + {content} + </ToastConfigProvider>, { ...options, id, diff --git a/src/components/Toast/index.web.tsx b/src/components/Toast/index.web.tsx index c4db20dca..8b2028db9 100644 --- a/src/components/Toast/index.web.tsx +++ b/src/components/Toast/index.web.tsx @@ -5,7 +5,9 @@ import {toast as sonner, Toaster} from 'sonner' import {atoms as a} from '#/alf' import {DURATION} from '#/components/Toast/const' import { - Default as DefaultToast, + Icon as ToastIcon, + Outer as ToastOuter, + Text as ToastText, ToastConfigProvider, } from '#/components/Toast/Toast' import {type BaseToastOptions} from '#/components/Toast/types' @@ -39,14 +41,17 @@ export const api = sonner */ export function show( content: React.ReactNode, - {type, ...options}: BaseToastOptions = {}, + {type = 'default', ...options}: BaseToastOptions = {}, ) { const id = nanoid() if (typeof content === 'string') { sonner( - <ToastConfigProvider id={id}> - <DefaultToast content={content} type={type} /> + <ToastConfigProvider id={id} type={type}> + <ToastOuter> + <ToastIcon /> + <ToastText>{content}</ToastText> + </ToastOuter> </ToastConfigProvider>, { ...options, @@ -56,12 +61,17 @@ export function show( }, ) } else if (React.isValidElement(content)) { - sonner(<ToastConfigProvider id={id}>{content}</ToastConfigProvider>, { - ...options, - unstyled: true, // required on web - id, - duration: options?.duration ?? DURATION, - }) + sonner( + <ToastConfigProvider id={id} type={type}> + {content} + </ToastConfigProvider>, + { + ...options, + unstyled: true, // required on web + id, + duration: options?.duration ?? DURATION, + }, + ) } else { throw new Error( `Toast can be a string or a React element, got ${typeof content}`, diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx index 7d4eb8ca7..6d22e4b54 100644 --- a/src/view/com/composer/Composer.tsx +++ b/src/view/com/composer/Composer.tsx @@ -126,7 +126,6 @@ import {Text} from '#/view/com/util/text/Text' import {UserAvatar} from '#/view/com/util/UserAvatar' import {atoms as a, native, useTheme, web} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' -import {CircleCheck_Stroke2_Corner0_Rounded as CircleCheckIcon} from '#/components/icons/CircleCheck' import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' import {EmojiArc_Stroke2_Corner0_Rounded as EmojiSmile} from '#/components/icons/Emoji' import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' @@ -527,8 +526,8 @@ export const ComposePost = ({ } onClose() Toast.show( - <Toast.Outer type="success"> - <Toast.Icon icon={CircleCheckIcon} /> + <Toast.Outer> + <Toast.Icon /> <Toast.Text> {thread.posts.length > 1 ? _(msg`Your posts were sent`) @@ -543,7 +542,9 @@ export const ComposePost = ({ const {host: name, rkey} = new AtUri(postUri) navigation.navigate('PostThread', {name, rkey}) }}> - View + <Trans context="Action to view the post the user just created"> + View + </Trans> </Toast.Action> )} </Toast.Outer>, diff --git a/src/view/screens/Storybook/Toasts.tsx b/src/view/screens/Storybook/Toasts.tsx index 319f88e21..91fe0d970 100644 --- a/src/view/screens/Storybook/Toasts.tsx +++ b/src/view/screens/Storybook/Toasts.tsx @@ -4,12 +4,28 @@ import {show as deprecatedShow} from '#/view/com/util/Toast' import {atoms as a} from '#/alf' import {Globe_Stroke2_Corner0_Rounded as GlobeIcon} from '#/components/icons/Globe' import * as Toast from '#/components/Toast' -import {Default} from '#/components/Toast/Toast' import {H1} from '#/components/Typography' -function ToastWithAction({type = 'default'}: {type?: Toast.ToastType}) { +function DefaultToast({ + content, + type = 'default', +}: { + content: string + type?: Toast.ToastType +}) { return ( - <Toast.Outer type={type}> + <Toast.ToastConfigProvider id="default-toast" type={type}> + <Toast.Outer> + <Toast.Icon icon={GlobeIcon} /> + <Toast.Text>{content}</Toast.Text> + </Toast.Outer> + </Toast.ToastConfigProvider> + ) +} + +function ToastWithAction() { + return ( + <Toast.Outer> <Toast.Icon icon={GlobeIcon} /> <Toast.Text>This toast has an action button</Toast.Text> <Toast.Action @@ -21,9 +37,9 @@ function ToastWithAction({type = 'default'}: {type?: Toast.ToastType}) { ) } -function LongToastWithAction({type = 'default'}: {type?: Toast.ToastType}) { +function LongToastWithAction() { return ( - <Toast.Outer type={type}> + <Toast.Outer> <Toast.Icon icon={GlobeIcon} /> <Toast.Text> This is a longer message to test how the toast handles multiple lines of @@ -44,33 +60,25 @@ export function Toasts() { <H1>Toast Examples</H1> <View style={[a.gap_md]}> - <View style={[a.gap_md, {marginHorizontal: a.px_xl.paddingLeft * -1}]}> - <Pressable - accessibilityRole="button" - onPress={() => Toast.show(<ToastWithAction />)}> - <ToastWithAction /> - </Pressable> - <Pressable - accessibilityRole="button" - onPress={() => Toast.show(<LongToastWithAction />)}> - <LongToastWithAction /> - </Pressable> - <Pressable - accessibilityRole="button" - onPress={() => Toast.show(<ToastWithAction type="success" />)}> - <ToastWithAction type="success" /> - </Pressable> - <Pressable - accessibilityRole="button" - onPress={() => Toast.show(<ToastWithAction type="error" />)}> - <ToastWithAction type="error" /> - </Pressable> - </View> - + <Pressable + accessibilityRole="button" + onPress={() => Toast.show(<ToastWithAction />, {type: 'success'})}> + <ToastWithAction /> + </Pressable> + <Pressable + accessibilityRole="button" + onPress={() => Toast.show(<ToastWithAction />, {type: 'error'})}> + <ToastWithAction /> + </Pressable> + <Pressable + accessibilityRole="button" + onPress={() => Toast.show(<LongToastWithAction />)}> + <LongToastWithAction /> + </Pressable> <Pressable accessibilityRole="button" onPress={() => Toast.show(`Hey I'm a toast!`)}> - <Default content="Hey I'm a toast!" /> + <DefaultToast content="Hey I'm a toast!" /> </Pressable> <Pressable accessibilityRole="button" @@ -79,7 +87,7 @@ export function Toasts() { duration: 6e3, }) }> - <Default content="This toast will disappear after 6 seconds" /> + <DefaultToast content="This toast will disappear after 6 seconds" /> </Pressable> <Pressable accessibilityRole="button" @@ -88,7 +96,7 @@ export function Toasts() { `This is a longer message to test how the toast handles multiple lines of text content.`, ) }> - <Default content="This is a longer message to test how the toast handles multiple lines of text content." /> + <DefaultToast content="This is a longer message to test how the toast handles multiple lines of text content." /> </Pressable> <Pressable accessibilityRole="button" @@ -97,7 +105,7 @@ export function Toasts() { type: 'success', }) }> - <Default content="Success! Yayyyyyyy :)" type="success" /> + <DefaultToast content="Success! Yayyyyyyy :)" type="success" /> </Pressable> <Pressable accessibilityRole="button" @@ -106,7 +114,7 @@ export function Toasts() { type: 'info', }) }> - <Default content="I'm providing info!" type="info" /> + <DefaultToast content="I'm providing info!" type="info" /> </Pressable> <Pressable accessibilityRole="button" @@ -115,7 +123,7 @@ export function Toasts() { type: 'warning', }) }> - <Default content="This is a warning toast" type="warning" /> + <DefaultToast content="This is a warning toast" type="warning" /> </Pressable> <Pressable accessibilityRole="button" @@ -124,7 +132,7 @@ export function Toasts() { type: 'error', }) }> - <Default content="This is an error toast :(" type="error" /> + <DefaultToast content="This is an error toast :(" type="error" /> </Pressable> <Pressable @@ -135,7 +143,7 @@ export function Toasts() { 'exclamation-circle', ) }> - <Default + <DefaultToast content="This is a test of the deprecated API" type="warning" /> |