Files
teatea-pension/modules/shared/components/UserAvatar.tsx

64 lines
1.5 KiB
TypeScript

import { cn } from "@/lib/utils";
type UserAvatarProps = {
name: string;
avatarUrl?: string;
size?: "sm" | "md" | "lg";
className?: string;
};
const sizeClasses: Record<NonNullable<UserAvatarProps["size"]>, string> = {
sm: "size-9 text-xs",
md: "size-10 text-sm",
lg: "size-14 text-lg",
};
function getInitial(name: string): string {
const trimmedName = name.trim();
if (!trimmedName) {
return "用";
}
return trimmedName.slice(0, 1).toUpperCase();
}
function isSupportedAvatarUrl(avatarUrl: string | undefined): avatarUrl is string {
if (!avatarUrl) {
return false;
}
return avatarUrl.startsWith("https://") || avatarUrl.startsWith("http://") || avatarUrl.startsWith("data:image/");
}
export function UserAvatar({
name,
avatarUrl,
size = "md",
className,
}: UserAvatarProps): React.ReactElement {
const hasAvatar = isSupportedAvatarUrl(avatarUrl);
return (
<span
aria-label={`${name || "用户"}头像`}
className={cn(
"inline-flex shrink-0 items-center justify-center overflow-hidden rounded-md border bg-secondary font-semibold text-primary",
sizeClasses[size],
className,
)}
role="img"
style={
hasAvatar
? {
backgroundImage: `url(${avatarUrl})`,
backgroundPosition: "center",
backgroundSize: "cover",
}
: undefined
}
>
{hasAvatar ? <span className="sr-only">{name}</span> : getInitial(name)}
</span>
);
}