'use client' import * as React from 'react' import * as AvatarPrimitive from '@radix-ui/react-avatar' import { cva, type VariantProps } from 'class-variance-authority' import { cn } from '@/lib/utils' const avatarVariants = cva( 'relative flex shrink-0 overflow-hidden', { variants: { size: { xs: 'size-6 text-[10px]', sm: 'size-8 text-xs', default: 'size-10 text-sm', lg: 'size-12 text-base', xl: 'size-16 text-lg', '2xl': 'size-24 text-2xl', }, shape: { circle: 'rounded-full', square: 'rounded-lg', hex: '[clip-path:polygon(25%_0%,75%_0%,100%_50%,75%_100%,25%_100%,0%_50%)]', }, ring: { none: '', default: 'ring-2 ring-border ring-offset-2 ring-offset-background', primary: 'ring-2 ring-primary ring-offset-2 ring-offset-background', gradient: 'p-0.5 bg-gradient-to-br from-primary to-secondary', story: 'p-0.5 bg-gradient-to-br from-primary via-secondary to-primary', live: 'p-0.5 bg-secondary animate-pulse', }, }, defaultVariants: { size: 'default', shape: 'circle', ring: 'none', }, }, ) interface AvatarProps extends React.ComponentProps, VariantProps { status?: 'online' | 'away' | 'busy' | 'offline' } function Avatar({ className, size, shape, ring, status, children, ...props }: AvatarProps) { const hasRingWithPadding = ring === 'gradient' || ring === 'story' || ring === 'live' return (
{children} {status && ( )}
) } function AvatarImage({ className, ...props }: React.ComponentProps) { return ( ) } function AvatarFallback({ className, ...props }: React.ComponentProps) { return ( ) } interface AvatarStatusProps { status: 'online' | 'away' | 'busy' | 'offline' size?: VariantProps['size'] } function AvatarStatus({ status, size }: AvatarStatusProps) { const statusStyles = { online: 'bg-success shadow-[0_0_8px_oklch(0.72_0.19_145_/_0.6)]', away: 'bg-warning', busy: 'bg-destructive', offline: 'bg-muted-foreground', } const sizeStyles = { xs: 'size-1.5 border', sm: 'size-2 border', default: 'size-2.5 border-2', lg: 'size-3 border-2', xl: 'size-4 border-2', '2xl': 'size-5 border-[3px]', } return ( ) } // Avatar Group for stacked avatars interface AvatarGroupProps extends React.ComponentProps<'div'> { max?: number size?: VariantProps['size'] } function AvatarGroup({ className, children, max = 4, size = 'default', ...props }: AvatarGroupProps) { const childArray = React.Children.toArray(children) const visibleChildren = childArray.slice(0, max) const remainingCount = childArray.length - max return (
{visibleChildren.map((child, index) => (
{React.isValidElement(child) ? React.cloneElement(child as React.ReactElement, { size, ring: 'default' }) : child }
))} {remainingCount > 0 && ( +{remainingCount} )}
) } export { Avatar, AvatarImage, AvatarFallback, AvatarStatus, AvatarGroup, avatarVariants }