All files / components/ui avatar.tsx

100% Statements 9/9
100% Branches 12/12
100% Functions 1/1
100% Lines 9/9

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83            3x               3x     3x                           16x 6x 6x                                   10x 10x   16x                                                        
import { cn } from "@/utils/class-name";
import {
  APP_ASSISTANT_AVATAR_ALT,
  APP_ASSISTANT_AVATAR_SRC,
} from "@/constants/app";
 
const SIZE = {
  sm: { box: "size-avatar-sm", text: "text-compact-10 tracking-wide" },
  md: { box: "h-9 w-9", text: "text-compact-11 tracking-wide" },
  lg: { box: "h-14 w-14", text: "text-sm tracking-wider" },
} as const;
 
export type AvatarSize = keyof typeof SIZE;
 
export const AVATAR_SIZE_OPTIONS = Object.keys(SIZE) as AvatarSize[];
 
const BASE =
  "relative shrink-0 select-none overflow-hidden rounded-full bg-cover bg-center";
 
type AvatarProps =
  | { variant: "assistant"; size?: AvatarSize; className?: string }
  | {
      variant: "user";
      src?: string;
      alt: string;
      initials: string;
      size?: AvatarSize;
      className?: string;
    };
 
export function Avatar(props: AvatarProps) {
  if (props.variant === "assistant") {
    const { size = "sm", className } = props;
    return (
      <div
        role="img"
        aria-label={APP_ASSISTANT_AVATAR_ALT}
        className={cn(
          BASE,
          SIZE[size].box,
          "border border-white/20 shadow-avatar-brand",
          "light:border-app-border-10",
          className,
        )}
        style={{
          backgroundImage: `url(${APP_ASSISTANT_AVATAR_SRC}), var(--avatar-assistant-fallback)`,
        }}
      />
    );
  }
 
  const { src, alt, initials, size = "sm", className } = props;
  const hasImage = Boolean(src?.trim());
 
  return (
    <div
      role="img"
      aria-label={alt}
      className={cn(
        BASE,
        SIZE[size].box,
        "bg-avatar-user-placeholder",
        "ring-[1.5px] ring-indigo-400/35 light:ring-stone-400/50",
        !hasImage && "shadow-avatar-placeholder",
        hasImage && "shadow-avatar-photo",
        className,
      )}
      style={hasImage ? { backgroundImage: `url(${src})` } : undefined}
    >
      {hasImage ? null : (
        <span
          className={cn(
            "absolute inset-0 flex items-center justify-center font-semibold text-white/90 light:text-white",
            SIZE[size].text,
          )}
        >
          {initials}
        </span>
      )}
    </div>
  );
}