️ feat: implement toast component

This commit is contained in:
Andre H 2024-02-22 14:16:47 +07:00
parent cb928c3360
commit 675112079c
2 changed files with 55 additions and 6 deletions

View File

@ -5,6 +5,7 @@ export const simpleToastTheme = tv(
slots: { slots: {
wrapper: [ wrapper: [
'flex', 'flex',
'items-center',
'py-2', 'py-2',
'pl-2', 'pl-2',
'pr-1.5', 'pr-1.5',
@ -18,6 +19,15 @@ export const simpleToastTheme = tv(
'shadow-sm', 'shadow-sm',
], ],
icon: ['flex', 'items-center', 'justify-center', 'w-5', 'h-5'], icon: ['flex', 'items-center', 'justify-center', 'w-5', 'h-5'],
closeIcon: [
'cursor-pointer',
'flex',
'items-center',
'justify-center',
'w-6',
'h-6',
'text-elements-on-high-contrast',
],
title: ['text-sm', 'text-elements-on-high-contrast'], title: ['text-sm', 'text-elements-on-high-contrast'],
}, },
variants: { variants: {

View File

@ -2,41 +2,80 @@ import React, { useMemo } from 'react';
import * as ToastPrimitive from '@radix-ui/react-toast'; import * as ToastPrimitive from '@radix-ui/react-toast';
import { ToastProps } from '@radix-ui/react-toast'; import { ToastProps } from '@radix-ui/react-toast';
import { simpleToastTheme, type SimpleToastTheme } from './SimpleToast.theme'; import { simpleToastTheme, type SimpleToastTheme } from './SimpleToast.theme';
import { CheckIcon, CheckRoundFilledIcon } from 'components/shared/CustomIcon'; import {
LoadingIcon,
CheckRoundFilledIcon,
CrossIcon,
InfoRoundFilledIcon,
WarningIcon,
} from 'components/shared/CustomIcon';
import { Button, ButtonBaseProps, ButtonTheme } from 'components/shared/Button';
import { cloneIcon } from 'utils/cloneIcon'; import { cloneIcon } from 'utils/cloneIcon';
import { cn } from 'utils/classnames';
interface CtaProps extends ButtonBaseProps, ButtonTheme {
buttonLabel: string;
}
export interface SimpleToastProps extends ToastProps { export interface SimpleToastProps extends ToastProps {
title: string; title: string;
variant?: SimpleToastTheme['variant']; variant?: SimpleToastTheme['variant'];
cta?: CtaProps[];
onDismiss: (id?: string) => void;
} }
export const SimpleToast = ({ export const SimpleToast = ({
className, className,
title, title,
variant = 'success', variant = 'success',
cta = [],
onDismiss,
...props ...props
}: SimpleToastProps) => { }: SimpleToastProps) => {
const hasCta = cta.length > 0;
const { const {
wrapper: wrapperCls, wrapper: wrapperCls,
icon: iconCls, icon: iconCls,
closeIcon: closeIconCls,
title: titleCls, title: titleCls,
} = simpleToastTheme({ variant }); } = simpleToastTheme({ variant });
const Icon = useMemo(() => { const Icon = useMemo(() => {
if (variant === 'success') return <CheckRoundFilledIcon />; if (variant === 'success') return <CheckRoundFilledIcon />;
if (variant === 'error') return <CheckIcon />; if (variant === 'error') return <WarningIcon />;
if (variant === 'warning') return <CheckIcon />; if (variant === 'warning') return <WarningIcon />;
if (variant === 'info') return <CheckIcon />; if (variant === 'info') return <InfoRoundFilledIcon />;
return <CheckIcon />; // variant === 'loading' return <LoadingIcon />; // variant === 'loading'
}, [variant]); }, [variant]);
const renderCta = useMemo(
() =>
hasCta ? (
<div className="flex gap-1.5 ml-2">
{cta.map(({ buttonLabel, ...props }, index) => (
<Button key={index} {...props}>
{buttonLabel}
</Button>
))}
</div>
) : null,
[],
);
const renderCloseButton = () => (
<div onClick={() => onDismiss(props.id)} className={closeIconCls()}>
<CrossIcon className="h-3 w-3" />
</div>
);
return ( return (
<ToastPrimitive.Root {...props} asChild> <ToastPrimitive.Root {...props} asChild>
<div className={wrapperCls({ class: className })}> <div className={wrapperCls({ class: cn(className) })}>
{cloneIcon(Icon, { className: iconCls() })} {cloneIcon(Icon, { className: iconCls() })}
<ToastPrimitive.Title asChild> <ToastPrimitive.Title asChild>
<p className={titleCls()}>{title}</p> <p className={titleCls()}>{title}</p>
</ToastPrimitive.Title> </ToastPrimitive.Title>
{renderCta}
{renderCloseButton()}
</div> </div>
</ToastPrimitive.Root> </ToastPrimitive.Root>
); );