Spaces:
Running
Running
File size: 2,792 Bytes
f23825d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
import { keyframes } from "@emotion/react"
import styled from "@emotion/styled"
import * as Portal from "@radix-ui/react-portal"
import Error from "mdi-react/AlertCircleIcon"
import Warning from "mdi-react/AlertIcon"
import CheckCircle from "mdi-react/CheckCircleIcon"
import Info from "mdi-react/InformationIcon"
import { FC, useEffect, useState } from "react"
import { Theme } from "../common/theme/Theme"
import { useTheme } from "../main/hooks/useTheme"
import { ToastSeverity } from "../main/hooks/useToast"
export interface ToastProps {
message: string
severity: ToastSeverity
onExited: () => void
}
const contentShow = keyframes`
from {
opacity: 0;
transform: translate(0, 0.5rem) scale(0.96);
}
to {
opacity: 1;
transform: translate(0, 0) scale(1);
}
`
const contentHide = keyframes`
from {
opacity: 1;
transform: translate(0, 0) scale(1);
}
to {
opacity: 0;
transform: translate(0, 0.5rem) scale(0.96);
}
`
const Root = styled(Portal.Root)`
position: fixed;
bottom: 2rem;
left: 0;
right: 0;
display: flex;
`
const Content = styled.div`
margin: 0 auto;
background: ${({ theme }) => theme.secondaryBackgroundColor};
padding: 1rem;
border-radius: 0.5rem;
font-size: 0.8rem;
box-shadow: 0 0.5rem 3rem ${({ theme }) => theme.shadowColor};
display: flex;
align-items: center;
animation: ${({ show }: { show: boolean }) =>
show ? contentShow : contentHide}
500ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
`
const SeverityIcon: FC<{ severity: ToastSeverity }> = ({ severity }) => {
const theme = useTheme()
const fill = colorForSeverity(severity, theme)
switch (severity) {
case "error":
return <Error style={{ fill }} />
case "info":
return <Info style={{ fill }} />
case "success":
return <CheckCircle style={{ fill }} />
case "warning":
return <Warning style={{ fill }} />
}
}
const exitDuration = 5000
export const Toast: FC<ToastProps> = ({ message, severity, onExited }) => {
const [show, setShow] = useState(true)
useEffect(() => {
const timeout = setTimeout(() => setShow(false), exitDuration - 500)
const timeout2 = setTimeout(onExited, exitDuration)
return () => {
clearTimeout(timeout)
clearTimeout(timeout2)
}
})
return (
<Root>
<Content show={show}>
<SeverityIcon severity={severity} />
<div style={{ width: "0.5rem" }} />
{message}
</Content>
</Root>
)
}
const colorForSeverity = (severity: ToastSeverity, theme: Theme) => {
switch (severity) {
case "error":
return theme.redColor
case "info":
return theme.textColor
case "success":
return theme.greenColor
case "warning":
return theme.yellowColor
}
}
|