import type { ReactNode } from "react"; import type { VariantProps } from "tailwind-variants"; import type { Accent } from "../../core/AlouetteConfig"; import { useAccentOrInheritedOrBrand } from "../../core/ThemeContext"; import { tv } from "../../core/tv"; import { Box } from "../containers/Box"; import { Icon, type SVGIconElement } from "../primitives/Icon"; import { Text } from "../primitives/Text"; const avatarVariants = tv({ slots: { frame: "flex-center shrink-0 rounded-full", label: "font-body-bold text-trim-cap", icon: "", }, variants: { size: { sm: { frame: "size-[28px]", label: "text-xs" }, md: { frame: "size-[32px]", label: "text-sm" }, lg: { frame: "size-[40px]", label: "text-base" }, }, variant: { solid: { frame: "bg-emphasis", label: "text-on-emphasis", icon: "text-on-emphasis", }, enabled: { frame: "bg-enabled", label: "text-on-accent", icon: "text-on-accent", }, }, }, defaultVariants: { size: "md", variant: "solid", }, }); type AvatarVariantProps = VariantProps; const avatarIconSize = { sm: 16, md: 18, lg: 22 }; function initialsFromName(name: string): string { return name .trim() .split(/\s+/) .slice(0, 2) .map((part) => part.slice(0, 1)) .join("") .toUpperCase(); } export interface AvatarProps extends AvatarVariantProps { /** Rendered as up to two initials, unless {@link icon} replaces them. */ name?: string; icon?: SVGIconElement; /** Accent of the disc. Defaults to the inherited accent, or `brand` outside * an accent scope. */ accent?: Accent; className?: string; } /** * Accent disc standing for a person or an account: their initials, or an icon. * Display-only — a pressable one is a `PressableBox` wrapped around it. */ export function Avatar({ name, icon, accent: accentProp, size, variant, className, }: AvatarProps): ReactNode { const accent = useAccentOrInheritedOrBrand(accentProp); const styles = avatarVariants({ size, variant }); return ( {icon ? ( ) : ( {name === undefined ? "" : initialsFromName(name)} )} ); }