Toast component and use for copy-as-curl

This commit is contained in:
Gregory Schier
2024-05-10 12:37:04 -07:00
parent b533a01677
commit 582da26574
14 changed files with 285 additions and 101 deletions

View File

@@ -60,9 +60,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button
size === 'sm' && 'h-sm px-2.5 text-sm',
size === 'xs' && 'h-xs px-2 text-sm',
// Solids
variant === 'solid' &&
color === 'custom' &&
'ring-blue-400 enabled:hocus:bg-highlightSecondary',
variant === 'solid' && color === 'custom' && 'ring-blue-400',
variant === 'solid' &&
color === 'default' &&
'text-gray-700 enabled:hocus:bg-gray-700/10 enabled:hocus:text-gray-800 ring-blue-400',

View File

@@ -17,7 +17,6 @@ const icons = {
arrowUpFromDot: lucide.ArrowUpFromDotIcon,
box: lucide.BoxIcon,
cake: lucide.CakeIcon,
minus: lucide.MinusIcon,
chat: lucide.MessageSquare,
check: lucide.CheckIcon,
chevronDown: lucide.ChevronDownIcon,
@@ -25,6 +24,7 @@ const icons = {
code: lucide.CodeIcon,
cookie: lucide.CookieIcon,
copy: lucide.CopyIcon,
copyCheck: lucide.CopyCheck,
download: lucide.DownloadIcon,
externalLink: lucide.ExternalLinkIcon,
eye: lucide.EyeIcon,
@@ -39,6 +39,7 @@ const icons = {
leftPanelHidden: lucide.PanelLeftOpenIcon,
leftPanelVisible: lucide.PanelLeftCloseIcon,
magicWand: lucide.Wand2Icon,
minus: lucide.MinusIcon,
moreVertical: lucide.MoreVerticalIcon,
pencil: lucide.PencilIcon,
plug: lucide.Plug,

View File

@@ -0,0 +1,76 @@
import classNames from 'classnames';
import { motion } from 'framer-motion';
import type { ReactNode } from 'react';
import React, { useMemo } from 'react';
import { useKey } from 'react-use';
import { Heading } from './Heading';
import { IconButton } from './IconButton';
export interface ToastProps {
children: ReactNode;
open: boolean;
onClose: () => void;
title?: ReactNode;
className?: string;
timeout: number;
}
export function Toast({ children, className, open, onClose, title, timeout }: ToastProps) {
const titleId = useMemo(() => Math.random().toString(36).slice(2), []);
useKey(
'Escape',
() => {
if (!open) return;
onClose();
},
{},
[open],
);
return (
<motion.div
initial={{ opacity: 0, right: '-10%' }}
animate={{ opacity: 100, right: 0 }}
exit={{ opacity: 0, right: '-100%' }}
transition={{ duration: 0.2 }}
className={classNames(
className,
'pointer-events-auto',
'relative bg-gray-50 dark:bg-gray-100 pointer-events-auto',
'rounded-lg',
'border border-highlightSecondary dark:border-highlight shadow-xl',
'max-w-[calc(100vw-5rem)] max-h-[calc(100vh-6rem)]',
'w-[22rem] max-h-[80vh]',
'm-2 grid grid-cols-[1fr_auto]',
'text-gray-700',
)}
>
<div className="px-3 py-2">
{title && (
<Heading size={3} id={titleId}>
{title}
</Heading>
)}
<div className="flex items-center gap-2">{children}</div>
</div>
<IconButton
color="custom"
className="opacity-50"
title="Dismiss"
icon="x"
onClick={onClose}
/>
<div className="w-full absolute bottom-0 left-0 right-0">
<motion.div
className="bg-highlight h-0.5"
initial={{ width: '100%' }}
animate={{ width: '0%', opacity: 0.2 }}
transition={{ duration: timeout / 1000, ease: 'linear' }}
/>
</div>
</motion.div>
);
}