103 lines
2.7 KiB
TypeScript
103 lines
2.7 KiB
TypeScript
import * as React from 'react';
|
|
import * as AvatarPrimitive from '@radix-ui/react-avatar';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
// Primitives
|
|
const Avatar = React.forwardRef<
|
|
React.ElementRef<typeof AvatarPrimitive.Root>,
|
|
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
|
|
>(({ className, ...props }, ref) => (
|
|
<AvatarPrimitive.Root
|
|
ref={ref}
|
|
className={cn(
|
|
'relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full',
|
|
className,
|
|
)}
|
|
{...props}
|
|
/>
|
|
));
|
|
Avatar.displayName = AvatarPrimitive.Root.displayName;
|
|
|
|
const AvatarImage = React.forwardRef<
|
|
React.ElementRef<typeof AvatarPrimitive.Image>,
|
|
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
|
|
>(({ className, ...props }, ref) => (
|
|
<AvatarPrimitive.Image
|
|
ref={ref}
|
|
className={cn('aspect-square h-full w-full object-cover', className)}
|
|
{...props}
|
|
/>
|
|
));
|
|
AvatarImage.displayName = AvatarPrimitive.Image.displayName;
|
|
|
|
const AvatarFallback = React.forwardRef<
|
|
React.ElementRef<typeof AvatarPrimitive.Fallback>,
|
|
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
|
|
>(({ className, ...props }, ref) => (
|
|
<AvatarPrimitive.Fallback
|
|
ref={ref}
|
|
className={cn(
|
|
'flex h-full w-full items-center justify-center rounded-full bg-muted',
|
|
className,
|
|
)}
|
|
{...props}
|
|
/>
|
|
));
|
|
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
|
|
|
|
// Smart Component (renamed to UserAvatar)
|
|
export interface UserAvatarProps {
|
|
src?: string | null;
|
|
alt?: string;
|
|
name?: string;
|
|
size?: 'sm' | 'md' | 'lg' | 'xl';
|
|
className?: string;
|
|
}
|
|
|
|
const sizeClasses = {
|
|
sm: 'w-8 h-8 text-xs',
|
|
md: 'w-12 h-12 text-sm',
|
|
lg: 'w-16 h-16 text-base',
|
|
xl: 'w-32 h-32 text-xl',
|
|
};
|
|
|
|
export function UserAvatar({
|
|
src,
|
|
alt,
|
|
name,
|
|
size = 'md',
|
|
className = '',
|
|
}: UserAvatarProps) {
|
|
const [imageError, setImageError] = React.useState(false);
|
|
const sizeClass = sizeClasses[size];
|
|
|
|
const getInitials = (name?: string): string => {
|
|
if (!name) return '?';
|
|
const parts = name.trim().split(' ');
|
|
if (parts.length >= 2) {
|
|
return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
|
|
}
|
|
return name.substring(0, 2).toUpperCase();
|
|
};
|
|
|
|
const initials = getInitials(name || alt);
|
|
|
|
return (
|
|
<Avatar className={cn(sizeClass, className)}>
|
|
{src && !imageError ? (
|
|
<AvatarImage
|
|
src={src}
|
|
alt={alt || name || 'Avatar'}
|
|
onLoadingStatusChange={(status) => {
|
|
if (status === 'error') setImageError(true);
|
|
}}
|
|
/>
|
|
) : null}
|
|
<AvatarFallback className="bg-gray-200 text-gray-600 font-medium">
|
|
{initials}
|
|
</AvatarFallback>
|
|
</Avatar>
|
|
);
|
|
}
|
|
|
|
export { Avatar, AvatarImage, AvatarFallback };
|