mirror of https://github.com/coder/coder.git
118 lines
2.5 KiB
TypeScript
118 lines
2.5 KiB
TypeScript
// This is the only place MuiAvatar can be used
|
|
// eslint-disable-next-line no-restricted-imports -- Read above
|
|
import MuiAvatar, {
|
|
type AvatarProps as MuiAvatarProps,
|
|
} from "@mui/material/Avatar";
|
|
import { type FC, useId } from "react";
|
|
import { css, type Interpolation, type Theme, useTheme } from "@emotion/react";
|
|
import { visuallyHidden } from "@mui/utils";
|
|
import { getExternalImageStylesFromUrl } from "theme/externalImages";
|
|
|
|
export type AvatarProps = MuiAvatarProps & {
|
|
size?: "xs" | "sm" | "md" | "xl";
|
|
background?: boolean;
|
|
fitImage?: boolean;
|
|
};
|
|
|
|
const sizeStyles = {
|
|
xs: {
|
|
width: 16,
|
|
height: 16,
|
|
fontSize: 8,
|
|
fontWeight: 700,
|
|
},
|
|
sm: {
|
|
width: 24,
|
|
height: 24,
|
|
fontSize: 12,
|
|
fontWeight: 600,
|
|
},
|
|
md: {},
|
|
xl: {
|
|
width: 48,
|
|
height: 48,
|
|
fontSize: 24,
|
|
},
|
|
} satisfies Record<string, Interpolation<Theme>>;
|
|
|
|
const fitImageStyles = css`
|
|
& .MuiAvatar-img {
|
|
object-fit: contain;
|
|
}
|
|
`;
|
|
|
|
export const Avatar: FC<AvatarProps> = ({
|
|
size = "md",
|
|
fitImage,
|
|
children,
|
|
background,
|
|
...muiProps
|
|
}) => {
|
|
const fromName = !muiProps.src && typeof children === "string";
|
|
|
|
return (
|
|
<MuiAvatar
|
|
{...muiProps}
|
|
css={[
|
|
sizeStyles[size],
|
|
fitImage && fitImageStyles,
|
|
(theme) => ({
|
|
background:
|
|
background || fromName ? theme.palette.divider : undefined,
|
|
color: theme.palette.text.primary,
|
|
}),
|
|
]}
|
|
>
|
|
{typeof children === "string" ? firstLetter(children) : children}
|
|
</MuiAvatar>
|
|
);
|
|
};
|
|
|
|
export const ExternalAvatar: FC<AvatarProps> = (props) => {
|
|
const theme = useTheme();
|
|
|
|
return (
|
|
<Avatar
|
|
css={getExternalImageStylesFromUrl(theme.externalImages, props.src)}
|
|
{...props}
|
|
/>
|
|
);
|
|
};
|
|
|
|
type AvatarIconProps = {
|
|
src: string;
|
|
alt: string;
|
|
};
|
|
|
|
/**
|
|
* Use it to make an img element behaves like a MaterialUI Icon component
|
|
*/
|
|
export const AvatarIcon: FC<AvatarIconProps> = ({ src, alt }) => {
|
|
const hookId = useId();
|
|
const avatarId = `${hookId}-avatar`;
|
|
|
|
// We use a `visuallyHidden` element instead of setting `alt` to avoid
|
|
// splatting the text out on the screen if the image fails to load.
|
|
return (
|
|
<>
|
|
<img
|
|
src={src}
|
|
alt=""
|
|
css={{ maxWidth: "50%" }}
|
|
aria-labelledby={avatarId}
|
|
/>
|
|
<div id={avatarId} css={{ ...visuallyHidden }}>
|
|
{alt}
|
|
</div>
|
|
</>
|
|
);
|
|
};
|
|
|
|
const firstLetter = (str: string): string => {
|
|
if (str.length > 0) {
|
|
return str[0].toLocaleUpperCase();
|
|
}
|
|
|
|
return "";
|
|
};
|