forked from cerc-io/snowballtools-base
⚡️ feat: implement toast component
This commit is contained in:
parent
cb928c3360
commit
675112079c
@ -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: {
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user