diff --git a/src/packages/Toaster/index.tsx b/src/packages/Toaster/index.tsx index 4ca8377..ef6eaa0 100644 --- a/src/packages/Toaster/index.tsx +++ b/src/packages/Toaster/index.tsx @@ -1,19 +1,29 @@ -import React, { useCallback, useContext, useEffect, useState } from 'react' +import React, { + useCallback, + useContext, + useEffect, + useMemo, + useRef, + useState +} from 'react' import { ThemeContext } from 'styled-components' import Alert, { AlertProps } from '../Alert' -import * as S from './styles' import ProgressBar from '../ProgressBar' +import * as S from './styles' + +const ONE_SECOND = 1000 + export type ToasterProps = { - duration?: number | null + duration?: number closable?: boolean initialVisible?: boolean -} & Omit // Omit the 'header' prop from AlertProps +} & Omit const Toaster = ({ - duration, + duration = 5000, closable = true, initialVisible = true, severity = 'success', @@ -21,18 +31,43 @@ const Toaster = ({ ...props }: ToasterProps) => { const [isVisible, setIsVisible] = useState(initialVisible) + const [progress, setProgress] = useState(0) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const timer = useRef() + + // eslint-disable-next-line react-hooks/exhaustive-deps + const startTimer = () => { + timer.current = setInterval(() => { + setProgress(progress + ONE_SECOND / 1000) + }, ONE_SECOND) + } + + const stopTimer = useCallback(() => { + if (timer.current) { + clearInterval(timer.current) + timer.current = null + } + }, []) + + const percentage = useMemo( + () => (progress / (duration / ONE_SECOND)) * 100, + [progress, duration] + ) useEffect(() => { - if (typeof duration === 'number') { - const timer = setTimeout(() => { - setIsVisible(false) - }, duration) - - return () => { - clearTimeout(timer) - } + startTimer() + return () => { + clearTimeout(timer.current) + timer.current = null } - }, [duration]) + }, [startTimer]) + + useEffect(() => { + if (percentage === 100) { + stopTimer() + setIsVisible(false) + } + }, [percentage, stopTimer]) // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore @@ -54,8 +89,13 @@ const Toaster = ({ closable={closable} severity={severity} onClose={() => handleClose()} + onMouseOver={() => stopTimer()} + onMouseOut={() => startTimer()} + /> + - ) } diff --git a/src/packages/Toaster/styles.ts b/src/packages/Toaster/styles.ts index 9615ded..2ec597c 100644 --- a/src/packages/Toaster/styles.ts +++ b/src/packages/Toaster/styles.ts @@ -1,7 +1,15 @@ -import styled from 'styled-components' +import styled, { css } from 'styled-components' export const Toaster = styled.div` - display: flex; - flex-direction: column; - width: 100%; + ${({ theme }) => css` + display: flex; + flex-direction: column; + width: 100%; + div[role='alert'] { + border-radius: ${theme.border.radius} ${theme.border.radius} 0 0; + } + div[role='progressbar'] { + border-radius: 0 0 ${theme.border.radius} ${theme.border.radius}; + } + `} `