import { type FC, type ReactNode, useState } from "react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faUser } from "@fortawesome/pro-duotone-svg-icons"; import { cx } from "@/utils/cx"; import { AvatarOnlineIndicator, VerifiedTick } from "./base-components"; type AvatarSize = "xxs" | "xs" | "sm" | "md" | "lg" | "xl" | "2xl"; export interface AvatarProps { size?: AvatarSize; className?: string; src?: string | null; alt?: string; /** * Display a contrast border around the avatar. */ contrastBorder?: boolean; /** * Display a badge (i.e. company logo). */ badge?: ReactNode; /** * Display a status indicator. */ status?: "online" | "offline"; /** * Display a verified tick icon. * * @default false */ verified?: boolean; /** * The initials of the user to display if no image is available. */ initials?: string; /** * An icon to display if no image is available. */ placeholderIcon?: FC<{ className?: string }>; /** * A placeholder to display if no image is available. */ placeholder?: ReactNode; /** * Whether the avatar should show a focus ring when the parent group is in focus. * For example, when the avatar is wrapped inside a link. * * @default false */ focusable?: boolean; } const styles = { xxs: { root: "size-4 outline-[0.5px] -outline-offset-[0.5px]", initials: "text-xs font-semibold", icon: "size-3" }, xs: { root: "size-6 outline-[0.5px] -outline-offset-[0.5px]", initials: "text-xs font-semibold", icon: "size-4" }, sm: { root: "size-8 outline-[0.75px] -outline-offset-[0.75px]", initials: "text-sm font-semibold", icon: "size-5" }, md: { root: "size-10 outline-1 -outline-offset-1", initials: "text-md font-semibold", icon: "size-6" }, lg: { root: "size-12 outline-1 -outline-offset-1", initials: "text-lg font-semibold", icon: "size-7" }, xl: { root: "size-14 outline-1 -outline-offset-1", initials: "text-xl font-semibold", icon: "size-8" }, "2xl": { root: "size-16 outline-1 -outline-offset-1", initials: "text-display-xs font-semibold", icon: "size-8" }, }; export const Avatar = ({ contrastBorder = true, size = "md", src, alt, initials, placeholder, placeholderIcon: PlaceholderIcon, badge, status, verified, focusable = false, className, }: AvatarProps) => { const [isFailed, setIsFailed] = useState(false); const renderMainContent = () => { if (src && !isFailed) { return {alt} setIsFailed(true)} />; } if (initials) { return {initials}; } if (PlaceholderIcon) { return ; } return placeholder || ; }; const renderBadgeContent = () => { if (status) { return ; } if (verified) { return ( ); } return badge; }; return (
{renderMainContent()} {renderBadgeContent()}
); };