import { useCallback, useMemo, useRef } from 'react'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { IconAlertTriangle, IconX } from '@/ui/icon'; import { ProgressBar, ProgressBarControls, } from '@/ui/progress-bar/components/ProgressBar'; import { rgba } from '@/ui/theme/constants/colors'; import { usePausableTimeout } from '../hooks/usePausableTimeout'; const StyledMotionContainer = styled.div>` align-items: center; background-color: ${({ theme, variant }) => { switch (variant) { case 'error': return theme.snackBar.error.background; case 'success': return theme.snackBar.success.background; case 'info': default: return theme.color.gray80; } }}; border-radius: ${({ theme }) => theme.border.radius.sm}; box-shadow: ${({ theme }) => theme.boxShadow.strong}; color: ${({ theme, variant }) => { switch (variant) { case 'error': return theme.snackBar.error.color; case 'success': return theme.snackBar.success.color; case 'info': default: return theme.grayScale.gray0; } }}; cursor: pointer; display: flex; height: 40px; overflow: hidden; padding: ${({ theme }) => theme.spacing(2)}; pointer-events: auto; position: relative; `; const StyledIconContainer = styled.div` display: flex; margin-right: ${({ theme }) => theme.spacing(2)}; `; const StyledProgressBarContainer = styled.div` height: 5px; left: 0; position: absolute; right: 0; top: 0; `; const StyledCloseButton = styled.button>` align-items: center; background-color: transparent; border: none; border-radius: 12px; color: ${({ theme, variant }) => { switch (variant) { case 'error': return theme.color.red20; case 'success': return theme.color.turquoise20; case 'info': default: return theme.grayScale.gray0; } }}; cursor: pointer; display: flex; height: 24px; justify-content: center; margin-left: ${({ theme }) => theme.spacing(6)}; padding-left: ${({ theme }) => theme.spacing(1)}; padding-right: ${({ theme }) => theme.spacing(1)}; width: 24px; &:hover { background-color: ${({ theme }) => rgba(theme.grayScale.gray0, 0.1)}; } `; export type SnackbarVariant = 'info' | 'error' | 'success'; export interface SnackbarProps extends React.ComponentPropsWithoutRef<'div'> { role?: 'alert' | 'status'; icon?: React.ReactNode; message?: string; allowDismiss?: boolean; duration?: number; variant?: SnackbarVariant; children?: React.ReactNode; onClose?: () => void; } export function SnackBar({ role = 'status', icon: iconComponent, message, allowDismiss = true, duration = 6000, variant = 'info', children, onClose, ...rootProps }: SnackbarProps) { const theme = useTheme(); const progressBarRef = useRef(null); const closeSnackbar = useCallback(() => { onClose && onClose(); }, [onClose]); const { pauseTimeout, resumeTimeout } = usePausableTimeout( closeSnackbar, duration, ); const icon = useMemo(() => { if (iconComponent) { return iconComponent; } switch (variant) { case 'error': return ( ); case 'success': case 'info': default: return null; } }, [iconComponent, theme.icon.size.md, variant]); const onMouseEnter = () => { progressBarRef.current?.pause(); pauseTimeout(); }; const onMouseLeave = () => { progressBarRef.current?.start(); resumeTimeout(); }; return ( {icon && {icon}} {children ? children : message} {allowDismiss && ( )} ); }