turash/bugulma/frontend/components/ui/Avatar.tsx
2025-12-15 10:06:41 +01:00

113 lines
2.5 KiB
TypeScript

import React from 'react';
import { clsx } from 'clsx';
import { User } from 'lucide-react';
export interface AvatarProps extends React.HTMLAttributes<HTMLDivElement> {
src?: string;
alt?: string;
name?: string;
size?: 'sm' | 'md' | 'lg' | 'xl';
fallback?: React.ReactNode;
status?: 'online' | 'offline' | 'away' | 'busy';
showStatus?: boolean;
}
const sizeClasses = {
sm: 'h-8 w-8 text-xs',
md: 'h-10 w-10 text-sm',
lg: 'h-12 w-12 text-base',
xl: 'h-16 w-16 text-lg',
};
const statusSizeClasses = {
sm: 'h-2 w-2',
md: 'h-2.5 w-2.5',
lg: 'h-3 w-3',
xl: 'h-3.5 w-3.5',
};
const statusStyles = {
online: 'bg-success',
offline: 'bg-muted-foreground',
away: 'bg-warning',
busy: 'bg-destructive',
};
/**
* Avatar component with image, fallback, and status indicator
*/
export const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>(
(
{
src,
alt,
name,
size = 'md',
fallback,
status = 'offline',
showStatus = false,
className,
...props
},
ref
) => {
const [imageError, setImageError] = React.useState(false);
const getInitials = (name: string) => {
return name
.split(' ')
.map((n) => n[0])
.join('')
.toUpperCase()
.slice(0, 2);
};
const displayFallback =
fallback ||
(name ? (
<span className="font-medium">{getInitials(name)}</span>
) : (
<User className="h-1/2 w-1/2" />
));
return (
<div
ref={ref}
className={clsx('relative inline-flex items-center justify-center', className)}
{...props}
>
<div
className={clsx(
'relative flex items-center justify-center rounded-full',
'bg-muted text-muted-foreground',
'overflow-hidden',
sizeClasses[size]
)}
>
{src && !imageError ? (
<img
src={src}
alt={alt || name || 'Avatar'}
className="h-full w-full object-cover"
onError={() => setImageError(true)}
/>
) : (
displayFallback
)}
</div>
{showStatus && (
<div
className={clsx(
'absolute bottom-0 right-0 rounded-full border-2 border-background',
statusStyles[status],
statusSizeClasses[size]
)}
aria-label={`Status: ${status}`}
/>
)}
</div>
);
}
);
Avatar.displayName = 'Avatar';