Files
Checkmate/Client/src/Components/Alert/index.jsx
T

129 lines
3.7 KiB
React
Raw Normal View History

2024-07-06 14:44:04 -04:00
import PropTypes from "prop-types";
2024-07-06 14:33:36 -04:00
import { useTheme } from "@emotion/react";
import { Box, IconButton, Stack, Typography } from "@mui/material";
2024-07-06 14:33:36 -04:00
import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
import ErrorOutlineOutlinedIcon from "@mui/icons-material/ErrorOutlineOutlined";
import WarningAmberOutlinedIcon from "@mui/icons-material/WarningAmberOutlined";
2024-07-11 16:45:29 -04:00
import CloseIcon from "@mui/icons-material/Close";
2024-07-06 14:33:36 -04:00
import "./index.css";
2024-07-11 16:45:29 -04:00
import Button from "../Button";
2024-07-06 14:33:36 -04:00
2024-07-06 14:44:04 -04:00
/**
* Icons mapping for different alert variants.
* @type {Object<string, JSX.Element>}
*/
2024-07-06 14:33:36 -04:00
const icons = {
info: <InfoOutlinedIcon />,
error: <ErrorOutlineOutlinedIcon />,
warning: <WarningAmberOutlinedIcon />,
};
2024-07-06 14:44:04 -04:00
/**
* @param {Object} props
* @param {'info' | 'error' | 'warning'} props.variant - The type of alert.
* @param {string} [props.title] - The title of the alert.
* @param {string} [props.body] - The body text of the alert.
2024-07-11 20:30:11 -04:00
* @param {boolean} [props.isToast] - Indicates if the alert is used as a toast notification.
* @param {boolean} [props.hasIcon] - Whether to display an icon in the alert.
* @param {function} props.onClick - Toast dismiss function.
2024-07-06 14:44:04 -04:00
* @returns {JSX.Element}
*/
const Alert = ({ variant, title, body, isToast, hasIcon = true, onClick }) => {
2024-07-06 14:33:36 -04:00
const theme = useTheme();
2024-08-27 12:18:25 -04:00
const { text, light, border } = theme.palette[variant];
2024-07-06 14:33:36 -04:00
const icon = icons[variant];
return (
<Stack
direction="row"
justifyContent="flex-start"
2024-07-11 16:45:29 -04:00
alignItems={hasIcon ? "" : "center"}
2024-07-06 14:33:36 -04:00
className="alert row-stack"
2024-08-27 12:18:25 -04:00
gap={theme.spacing(8)}
2024-07-06 14:33:36 -04:00
sx={{
2024-08-27 12:18:25 -04:00
padding: hasIcon
? theme.spacing(8)
: `${theme.spacing(4)} ${theme.spacing(8)}`,
backgroundColor: light,
2024-07-06 14:33:36 -04:00
border: `solid 1px ${border}`,
2024-08-27 12:18:25 -04:00
borderRadius: theme.shape.borderRadius,
2024-07-06 14:33:36 -04:00
}}
>
2024-08-27 12:18:25 -04:00
{hasIcon && <Box sx={{ color: text }}>{icon}</Box>}
<Stack direction="column" gap="2px" sx={{ flex: 1 }}>
{title && (
2024-08-27 12:18:25 -04:00
<Typography sx={{ fontWeight: "700", color: `${text}` }}>
{title}
</Typography>
)}
{body && (
2024-08-27 12:18:25 -04:00
<Typography sx={{ fontWeight: "400", color: `${text}` }}>
{body}
</Typography>
)}
{hasIcon && isToast && (
2024-07-11 16:45:29 -04:00
<Button
level="tertiary"
label="Dismiss"
onClick={onClick}
sx={{
fontWeight: "600",
width: "fit-content",
2024-08-27 12:18:25 -04:00
mt: theme.spacing(4),
2024-07-11 16:45:29 -04:00
padding: 0,
minWidth: 0,
"&:hover": {
backgroundColor: "transparent",
},
"& .MuiTouchRipple-root": {
pointerEvents: "none",
display: "none",
},
}}
></Button>
)}
2024-07-06 14:33:36 -04:00
</Stack>
{isToast && (
2024-07-11 16:45:29 -04:00
<IconButton
onClick={onClick}
sx={{
alignSelf: "flex-start",
ml: "auto",
mr: "-5px",
mt: hasIcon ? "-5px" : 0,
padding: "5px",
"&:focus": {
outline: "none",
},
}}
>
<CloseIcon
sx={{
fontSize: "20px",
}}
/>
</IconButton>
)}
2024-07-06 14:33:36 -04:00
</Stack>
);
};
2024-07-06 14:44:04 -04:00
Alert.propTypes = {
variant: PropTypes.oneOf(["info", "error", "warning"]).isRequired,
title: PropTypes.string,
body: PropTypes.string,
2024-07-11 20:30:11 -04:00
isToast: PropTypes.bool,
hasIcon: PropTypes.bool,
onClick: function (props, propName, componentName) {
if (props.isToast && !props[propName]) {
return new Error(
`Prop '${propName}' is required when 'isToast' is true in '${componentName}'.`
);
}
return null;
},
2024-07-06 14:44:04 -04:00
};
2024-07-06 14:33:36 -04:00
export default Alert;