65 lines
1.7 KiB
TypeScript
65 lines
1.7 KiB
TypeScript
import React from 'react';
|
|
|
|
interface AvatarProps {
|
|
name: string;
|
|
imageUrl?: string;
|
|
size?: 'sm' | 'md' | 'lg';
|
|
shape?: 'circle' | 'square';
|
|
}
|
|
|
|
const SIZE_MAP = {
|
|
sm: 'w-8 h-8 text-xs',
|
|
md: 'w-12 h-12 text-sm',
|
|
lg: 'w-20 h-20 text-lg',
|
|
};
|
|
|
|
const COLORS = [
|
|
'bg-red-500', 'bg-orange-500', 'bg-amber-500', 'bg-yellow-500',
|
|
'bg-lime-500', 'bg-green-500', 'bg-emerald-500', 'bg-teal-500',
|
|
'bg-cyan-500', 'bg-sky-500', 'bg-blue-500', 'bg-indigo-500',
|
|
'bg-violet-500', 'bg-purple-500', 'bg-fuchsia-500', 'bg-pink-500',
|
|
];
|
|
|
|
const getInitials = (name: string) => {
|
|
const parts = name.trim().split(/\s+/);
|
|
if (parts.length === 1) return parts[0].substring(0, 2).toUpperCase();
|
|
return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
|
|
};
|
|
|
|
const getColorByName = (name: string) => {
|
|
let hash = 0;
|
|
for (let i = 0; i < name.length; i++) {
|
|
hash = name.charCodeAt(i) + ((hash << 5) - hash);
|
|
}
|
|
const index = Math.abs(hash) % COLORS.length;
|
|
return COLORS[index];
|
|
};
|
|
|
|
export default function Avatar({
|
|
name,
|
|
imageUrl,
|
|
size = 'md',
|
|
shape = 'circle'
|
|
}: AvatarProps) {
|
|
const sizeClass = SIZE_MAP[size];
|
|
const shapeClass = shape === 'circle' ? 'rounded-full' : 'rounded-md';
|
|
const bgColor = getColorByName(name);
|
|
const initials = getInitials(name);
|
|
|
|
return (
|
|
<div className={`${sizeClass} ${shapeClass} ${bgColor} flex items-center justify-center shrink-0 overflow-hidden text-white font-medium`}>
|
|
{imageUrl ? (
|
|
<img
|
|
src={imageUrl}
|
|
alt={name}
|
|
className="w-full h-full object-cover"
|
|
onError={(e) => {
|
|
(e.target as HTMLImageElement).style.display = 'none';
|
|
}}
|
|
/>
|
|
) : (
|
|
<span>{initials}</span>
|
|
)}
|
|
</div>
|
|
);
|
|
} |