({ name, color, avatar, role, isActive = true }: AvatarProps)
| 19 | } |
| 20 | |
| 21 | function UserAvatar({ name, color, avatar, role, isActive = true }: AvatarProps) { |
| 22 | return ( |
| 23 | <Tooltip.Provider delayDuration={300}> |
| 24 | <Tooltip.Root> |
| 25 | <Tooltip.Trigger asChild> |
| 26 | <div |
| 27 | className="relative w-7 h-7 rounded-full flex-shrink-0 cursor-default select-none" |
| 28 | style={{ boxShadow: `0 0 0 2px ${color}` }} |
| 29 | > |
| 30 | {avatar ? ( |
| 31 | // eslint-disable-next-line @next/next/no-img-element |
| 32 | <img |
| 33 | src={avatar} |
| 34 | alt={name} |
| 35 | className="w-full h-full rounded-full object-cover" |
| 36 | /> |
| 37 | ) : ( |
| 38 | <div |
| 39 | className="w-full h-full rounded-full flex items-center justify-center text-[10px] font-semibold text-white" |
| 40 | style={{ backgroundColor: color }} |
| 41 | > |
| 42 | {getInitials(name)} |
| 43 | </div> |
| 44 | )} |
| 45 | |
| 46 | {/* Online indicator dot */} |
| 47 | {isActive && ( |
| 48 | <span className="absolute bottom-0 right-0 w-2 h-2 rounded-full bg-green-400 border border-surface-900" /> |
| 49 | )} |
| 50 | </div> |
| 51 | </Tooltip.Trigger> |
| 52 | |
| 53 | <Tooltip.Portal> |
| 54 | <Tooltip.Content |
| 55 | side="bottom" |
| 56 | sideOffset={6} |
| 57 | className={cn( |
| 58 | "z-50 rounded-md px-2.5 py-1.5 text-xs shadow-md", |
| 59 | "bg-surface-800 border border-surface-700 text-surface-100" |
| 60 | )} |
| 61 | > |
| 62 | <p className="font-medium">{name}</p> |
| 63 | <p className="text-surface-400">{labelForRole(role)}</p> |
| 64 | <Tooltip.Arrow className="fill-surface-800" /> |
| 65 | </Tooltip.Content> |
| 66 | </Tooltip.Portal> |
| 67 | </Tooltip.Root> |
| 68 | </Tooltip.Provider> |
| 69 | ); |
| 70 | } |
| 71 | |
| 72 | // ─── PresenceAvatars ────────────────────────────────────────────────────────── |
| 73 |
nothing calls this directly
no test coverage detected