mirror of https://github.com/coder/coder.git
131 lines
3.2 KiB
TypeScript
131 lines
3.2 KiB
TypeScript
import ListItem from "@mui/material/ListItem";
|
|
import { makeStyles } from "@mui/styles";
|
|
import CheckIcon from "@mui/icons-material/Check";
|
|
import { FC } from "react";
|
|
import { NavLink } from "react-router-dom";
|
|
import { ellipsizeText } from "../../../../../utils/ellipsizeText";
|
|
import { Typography } from "../../../../Typography/Typography";
|
|
|
|
type BorderedMenuRowVariant = "narrow" | "wide";
|
|
|
|
interface BorderedMenuRowProps {
|
|
/** `true` indicates this row is currently selected */
|
|
active?: boolean;
|
|
/** Optional description that appears beneath the title */
|
|
description?: string;
|
|
/** URL path */
|
|
path: string;
|
|
/** Required title of this row */
|
|
title: string;
|
|
/** Defaults to `"wide"` */
|
|
variant?: BorderedMenuRowVariant;
|
|
/** Callback fired when this row is clicked */
|
|
onClick?: () => void;
|
|
}
|
|
|
|
export const BorderedMenuRow: FC<
|
|
React.PropsWithChildren<BorderedMenuRowProps>
|
|
> = ({ active, description, path, title, variant, onClick }) => {
|
|
const styles = useStyles();
|
|
|
|
return (
|
|
<NavLink className={styles.link} to={path}>
|
|
<ListItem
|
|
classes={{ gutters: styles.rootGutters }}
|
|
className={styles.root}
|
|
data-status={active ? "active" : "inactive"}
|
|
onClick={onClick}
|
|
>
|
|
<div className={styles.content} data-variant={variant}>
|
|
<div className={styles.contentTop}>
|
|
<Typography className={styles.title}>{title}</Typography>
|
|
{active && <CheckIcon className={styles.checkMark} />}
|
|
</div>
|
|
|
|
{description && (
|
|
<Typography
|
|
className={styles.description}
|
|
color="textSecondary"
|
|
variant="caption"
|
|
>
|
|
{ellipsizeText(description)}
|
|
</Typography>
|
|
)}
|
|
</div>
|
|
</ListItem>
|
|
</NavLink>
|
|
);
|
|
};
|
|
|
|
const iconSize = 20;
|
|
|
|
const useStyles = makeStyles((theme) => ({
|
|
root: {
|
|
cursor: "pointer",
|
|
padding: `0 ${theme.spacing(1)}`,
|
|
|
|
"&:hover": {
|
|
backgroundColor: "unset",
|
|
"& $content": {
|
|
backgroundColor: theme.palette.background.default,
|
|
},
|
|
},
|
|
|
|
"&[data-status='active']": {
|
|
color: theme.palette.secondary.dark,
|
|
"& .BorderedMenuRow-description": {
|
|
color: theme.palette.text.primary,
|
|
},
|
|
"& .BorderedMenuRow-icon": {
|
|
color: theme.palette.secondary.dark,
|
|
},
|
|
},
|
|
},
|
|
rootGutters: {
|
|
padding: `0 ${theme.spacing(1.5)}`,
|
|
},
|
|
content: {
|
|
borderRadius: theme.shape.borderRadius,
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
padding: theme.spacing(2),
|
|
width: 320,
|
|
|
|
"&[data-variant='narrow']": {
|
|
width: 268,
|
|
},
|
|
},
|
|
contentTop: {
|
|
alignItems: "center",
|
|
display: "flex",
|
|
},
|
|
icon: {
|
|
color: theme.palette.text.secondary,
|
|
height: iconSize,
|
|
width: iconSize,
|
|
|
|
"& path": {
|
|
fill: theme.palette.text.secondary,
|
|
},
|
|
},
|
|
link: {
|
|
textDecoration: "none",
|
|
color: "inherit",
|
|
},
|
|
title: {
|
|
fontSize: 16,
|
|
fontWeight: 500,
|
|
lineHeight: 1.5,
|
|
marginLeft: theme.spacing(2),
|
|
},
|
|
checkMark: {
|
|
height: iconSize,
|
|
marginLeft: "auto",
|
|
width: iconSize,
|
|
},
|
|
description: {
|
|
marginLeft: theme.spacing(4.5),
|
|
marginTop: theme.spacing(0.5),
|
|
},
|
|
}));
|