mirror of https://github.com/coder/coder.git
103 lines
2.9 KiB
TypeScript
103 lines
2.9 KiB
TypeScript
import type { Interpolation, Theme } from "@emotion/react";
|
|
import CloseIcon from "@mui/icons-material/Close";
|
|
import IconButton from "@mui/material/IconButton";
|
|
import Snackbar, {
|
|
type SnackbarProps as MuiSnackbarProps,
|
|
} from "@mui/material/Snackbar";
|
|
import type { FC } from "react";
|
|
import { type ClassName, useClassName } from "hooks/useClassName";
|
|
|
|
type EnterpriseSnackbarVariant = "error" | "info" | "success";
|
|
|
|
export interface EnterpriseSnackbarProps extends MuiSnackbarProps {
|
|
/** Called when the snackbar should close, either from timeout or clicking close */
|
|
onClose: () => void;
|
|
/** Variant of snackbar, for theming */
|
|
variant?: EnterpriseSnackbarVariant;
|
|
}
|
|
|
|
/**
|
|
* Wrapper around Material UI's Snackbar component, provides pre-configured
|
|
* themes and convenience props. Coder UI's Snackbars require a close handler,
|
|
* since they always render a close button.
|
|
*
|
|
* Snackbars do _not_ automatically appear in the top-level position when
|
|
* rendered, you'll need to use ReactDom portals or the Material UI Portal
|
|
* component for that.
|
|
*
|
|
* See original component's Material UI documentation here: https://material-ui.com/components/snackbars/
|
|
*/
|
|
export const EnterpriseSnackbar: FC<EnterpriseSnackbarProps> = ({
|
|
children,
|
|
onClose,
|
|
variant = "info",
|
|
ContentProps = {},
|
|
action,
|
|
...snackbarProps
|
|
}) => {
|
|
const content = useClassName(classNames.content(variant), [variant]);
|
|
|
|
return (
|
|
<Snackbar
|
|
anchorOrigin={{
|
|
vertical: "bottom",
|
|
horizontal: "right",
|
|
}}
|
|
action={
|
|
<div css={styles.actionWrapper}>
|
|
{action}
|
|
<IconButton onClick={onClose} css={{ padding: 0 }}>
|
|
<CloseIcon css={styles.closeIcon} aria-label="close" />
|
|
</IconButton>
|
|
</div>
|
|
}
|
|
ContentProps={{
|
|
...ContentProps,
|
|
className: content,
|
|
}}
|
|
onClose={onClose}
|
|
{...snackbarProps}
|
|
>
|
|
{children}
|
|
</Snackbar>
|
|
);
|
|
};
|
|
|
|
const variantColor = (variant: EnterpriseSnackbarVariant, theme: Theme) => {
|
|
switch (variant) {
|
|
case "error":
|
|
return theme.palette.error.main;
|
|
case "info":
|
|
return theme.palette.info.main;
|
|
case "success":
|
|
return theme.palette.success.main;
|
|
}
|
|
};
|
|
|
|
const classNames = {
|
|
content:
|
|
(variant: EnterpriseSnackbarVariant): ClassName =>
|
|
(css, theme) => css`
|
|
border: 1px solid ${theme.palette.divider};
|
|
border-left: 4px solid ${variantColor(variant, theme)};
|
|
border-radius: 8px;
|
|
padding: 8px 24px 8px 16px;
|
|
box-shadow: ${theme.shadows[6]};
|
|
align-items: inherit;
|
|
background-color: ${theme.palette.background.paper};
|
|
color: ${theme.palette.text.secondary};
|
|
`,
|
|
};
|
|
|
|
const styles = {
|
|
actionWrapper: {
|
|
display: "flex",
|
|
alignItems: "center",
|
|
},
|
|
closeIcon: (theme) => ({
|
|
width: 25,
|
|
height: 25,
|
|
color: theme.palette.primary.contrastText,
|
|
}),
|
|
} satisfies Record<string, Interpolation<Theme>>;
|