From 3bcfcba6d8176bac03202b496110915da748b0f1 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Thu, 31 Jul 2025 10:15:35 -0500 Subject: Some toasts cleanup and reorg (#8748) * Reorg * Move animation into css file * Update style comment * Extract core component, use platform-specific wrappers * Pull out platform specific styles * Just move styles into Toast component itself * Rename cleanup * Update API * Add duration optional prop * Add some type docs * add exp eased slide aniamtions * Make toasts full width on mobile web --------- Co-authored-by: Samuel Newman --- src/components/Toast/Toast.tsx | 205 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 src/components/Toast/Toast.tsx (limited to 'src/components/Toast/Toast.tsx') diff --git a/src/components/Toast/Toast.tsx b/src/components/Toast/Toast.tsx new file mode 100644 index 000000000..0dc9d4b07 --- /dev/null +++ b/src/components/Toast/Toast.tsx @@ -0,0 +1,205 @@ +import {createContext, useContext, useMemo} from 'react' +import {View} from 'react-native' + +import {atoms as a, select, useTheme} from '#/alf' +import {Check_Stroke2_Corner0_Rounded as SuccessIcon} from '#/components/icons/Check' +import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' +import {CircleInfo_Stroke2_Corner0_Rounded as ErrorIcon} from '#/components/icons/CircleInfo' +import {Warning_Stroke2_Corner0_Rounded as WarningIcon} from '#/components/icons/Warning' +import {type ToastType} from '#/components/Toast/types' +import {Text} from '#/components/Typography' + +type ContextType = { + type: ToastType +} + +export const ICONS = { + default: SuccessIcon, + success: SuccessIcon, + error: ErrorIcon, + warning: WarningIcon, + info: CircleInfo, +} + +const Context = createContext({ + type: 'default', +}) + +export function Toast({ + type, + content, +}: { + type: ToastType + content: React.ReactNode +}) { + const t = useTheme() + const styles = useToastStyles({type}) + const Icon = ICONS[type] + + return ( + ({type}), [type])}> + + + + + {typeof content === 'string' ? ( + {content} + ) : ( + content + )} + + + + ) +} + +export function ToastText({children}: {children: React.ReactNode}) { + const {type} = useContext(Context) + const {textColor} = useToastStyles({type}) + return ( + + {children} + + ) +} + +function useToastStyles({type}: {type: ToastType}) { + const t = useTheme() + return useMemo(() => { + return { + default: { + backgroundColor: select(t.name, { + light: t.atoms.bg_contrast_25.backgroundColor, + dim: t.atoms.bg_contrast_100.backgroundColor, + dark: t.atoms.bg_contrast_100.backgroundColor, + }), + borderColor: select(t.name, { + light: t.atoms.border_contrast_low.borderColor, + dim: t.atoms.border_contrast_high.borderColor, + dark: t.atoms.border_contrast_high.borderColor, + }), + iconColor: select(t.name, { + light: t.atoms.text_contrast_medium.color, + dim: t.atoms.text_contrast_medium.color, + dark: t.atoms.text_contrast_medium.color, + }), + textColor: select(t.name, { + light: t.atoms.text_contrast_medium.color, + dim: t.atoms.text_contrast_medium.color, + dark: t.atoms.text_contrast_medium.color, + }), + }, + success: { + backgroundColor: select(t.name, { + light: t.palette.primary_100, + dim: t.palette.primary_100, + dark: t.palette.primary_50, + }), + borderColor: select(t.name, { + light: t.palette.primary_500, + dim: t.palette.primary_500, + dark: t.palette.primary_500, + }), + iconColor: select(t.name, { + light: t.palette.primary_500, + dim: t.palette.primary_600, + dark: t.palette.primary_600, + }), + textColor: select(t.name, { + light: t.palette.primary_500, + dim: t.palette.primary_600, + dark: t.palette.primary_600, + }), + }, + error: { + backgroundColor: select(t.name, { + light: t.palette.negative_200, + dim: t.palette.negative_25, + dark: t.palette.negative_25, + }), + borderColor: select(t.name, { + light: t.palette.negative_300, + dim: t.palette.negative_300, + dark: t.palette.negative_300, + }), + iconColor: select(t.name, { + light: t.palette.negative_600, + dim: t.palette.negative_600, + dark: t.palette.negative_600, + }), + textColor: select(t.name, { + light: t.palette.negative_600, + dim: t.palette.negative_600, + dark: t.palette.negative_600, + }), + }, + warning: { + backgroundColor: select(t.name, { + light: t.atoms.bg_contrast_25.backgroundColor, + dim: t.atoms.bg_contrast_100.backgroundColor, + dark: t.atoms.bg_contrast_100.backgroundColor, + }), + borderColor: select(t.name, { + light: t.atoms.border_contrast_low.borderColor, + dim: t.atoms.border_contrast_high.borderColor, + dark: t.atoms.border_contrast_high.borderColor, + }), + iconColor: select(t.name, { + light: t.atoms.text_contrast_medium.color, + dim: t.atoms.text_contrast_medium.color, + dark: t.atoms.text_contrast_medium.color, + }), + textColor: select(t.name, { + light: t.atoms.text_contrast_medium.color, + dim: t.atoms.text_contrast_medium.color, + dark: t.atoms.text_contrast_medium.color, + }), + }, + info: { + backgroundColor: select(t.name, { + light: t.atoms.bg_contrast_25.backgroundColor, + dim: t.atoms.bg_contrast_100.backgroundColor, + dark: t.atoms.bg_contrast_100.backgroundColor, + }), + borderColor: select(t.name, { + light: t.atoms.border_contrast_low.borderColor, + dim: t.atoms.border_contrast_high.borderColor, + dark: t.atoms.border_contrast_high.borderColor, + }), + iconColor: select(t.name, { + light: t.atoms.text_contrast_medium.color, + dim: t.atoms.text_contrast_medium.color, + dark: t.atoms.text_contrast_medium.color, + }), + textColor: select(t.name, { + light: t.atoms.text_contrast_medium.color, + dim: t.atoms.text_contrast_medium.color, + dark: t.atoms.text_contrast_medium.color, + }), + }, + }[type] + }, [t, type]) +} -- cgit 1.4.1