UNPKG

@frank-auth/react

Version:

Flexible and customizable React UI components for Frank Authentication

1 lines 14.2 kB
{"version":3,"file":"user-avatar.cjs","sources":["../../../../../src/components/auth/user-button/user-avatar.tsx"],"sourcesContent":["/**\n * @frank-auth/react - User Avatar Component\n *\n * Customizable user avatar component with fallback initials,\n * online status, and organization-specific styling.\n */\n\n'use client';\n\nimport React from 'react';\nimport {Avatar, Badge} from '@heroui/react';\nimport {useAuth} from '../../../hooks/use-auth';\nimport {useConfig} from '../../../hooks/use-config';\nimport {useTheme} from '../../../hooks/use-theme';\n\n// ============================================================================\n// User Avatar Interface\n// ============================================================================\n\nexport interface UserAvatarProps {\n /**\n * User ID (if different from current user)\n */\n userId?: string;\n\n /**\n * User profile image URL\n */\n src?: string;\n\n /**\n * User name for fallback initials\n */\n name?: string;\n\n /**\n * User email for fallback initials\n */\n email?: string;\n\n /**\n * Avatar size\n */\n size?: 'sm' | 'md' | 'lg';\n\n /**\n * Avatar radius\n */\n radius?: 'none' | 'sm' | 'md' | 'lg' | 'full';\n\n /**\n * Show online status\n */\n showStatus?: boolean;\n\n /**\n * Online status\n */\n isOnline?: boolean;\n\n /**\n * Custom fallback content\n */\n fallback?: React.ReactNode;\n\n /**\n * Custom className\n */\n className?: string;\n\n /**\n * Click handler\n */\n onClick?: () => void;\n\n /**\n * Whether avatar is clickable\n */\n isClickable?: boolean;\n\n /**\n * Show organization badge\n */\n showOrganizationBadge?: boolean;\n\n /**\n * Organization role to display\n */\n role?: string;\n\n /**\n * Color scheme\n */\n color?: 'default' | 'primary' | 'secondary' | 'success' | 'warning' | 'danger';\n\n /**\n * Whether to show border\n */\n isBordered?: boolean;\n\n /**\n * Whether avatar is disabled\n */\n isDisabled?: boolean;\n\n /**\n * Custom initials\n */\n initials?: string;\n\n /**\n * Fallback icon when no image or initials\n */\n icon?: React.ReactNode;\n\n /**\n * Avatar quality for image\n */\n quality?: number;\n\n /**\n * Whether to use organization colors\n */\n useOrganizationColors?: boolean;\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Generate initials from name or email\n */\nfunction generateInitials(name?: string, email?: string): string {\n if (name) {\n const parts = name.trim().split(' ');\n if (parts.length >= 2) {\n return `${parts[0][0]}${parts[1][0]}`.toUpperCase();\n }\n return parts[0][0]?.toUpperCase() || '';\n }\n\n if (email) {\n return email[0]?.toUpperCase() || '';\n }\n\n return '';\n}\n\n/**\n * Get role color based on role type\n */\nfunction getRoleColor(role?: string): 'default' | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' {\n switch (role?.toLowerCase()) {\n case 'owner':\n return 'danger';\n case 'admin':\n return 'warning';\n case 'member':\n return 'primary';\n case 'guest':\n return 'secondary';\n default:\n return 'default';\n }\n}\n\n/**\n * Get role label for display\n */\nfunction getRoleLabel(role?: string): string {\n switch (role?.toLowerCase()) {\n case 'owner':\n return 'Owner';\n case 'admin':\n return 'Admin';\n case 'member':\n return 'Member';\n case 'guest':\n return 'Guest';\n default:\n return role || '';\n }\n}\n\n// ============================================================================\n// User Avatar Component\n// ============================================================================\n\nexport function UserAvatar({\n userId,\n src,\n name,\n email,\n size = 'md',\n radius = 'full',\n showStatus = false,\n isOnline = false,\n fallback,\n className = '',\n onClick,\n isClickable = false,\n showOrganizationBadge = false,\n role,\n color = 'default',\n isBordered = false,\n isDisabled = false,\n initials: customInitials,\n icon,\n quality = 80,\n useOrganizationColors = false,\n }: UserAvatarProps) {\n const { user, isOrganizationMember } = useAuth();\n const { components, organizationSettings } = useConfig();\n const { primaryColor } = useTheme();\n\n // Custom component override\n const CustomUserAvatar = components.UserAvatar;\n if (CustomUserAvatar) {\n return <CustomUserAvatar {...{\n userId, src, name, email, size, radius, showStatus, isOnline, fallback,\n className, onClick, isClickable, showOrganizationBadge, role, color,\n isBordered, isDisabled, initials: customInitials, icon, quality, useOrganizationColors\n }} />;\n }\n\n // Use current user data if no specific user provided\n const effectiveUser = userId ? null : user;\n const effectiveSrc = src || effectiveUser?.profileImageUrl;\n const effectiveName = name || effectiveUser?.firstName && effectiveUser?.lastName\n ? `${effectiveUser.firstName} ${effectiveUser.lastName}`\n : effectiveUser?.username;\n const effectiveEmail = email || effectiveUser?.primaryEmailAddress;\n\n // Generate initials\n const displayInitials = customInitials || generateInitials(effectiveName, effectiveEmail);\n\n // Apply organization colors if enabled\n const effectiveColor = useOrganizationColors && organizationSettings?.branding?.primaryColor\n ? 'primary'\n : color;\n\n // Default user icon\n const defaultIcon = icon || (\n <svg\n className=\"w-full h-full text-default-400\"\n fill=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path d=\"M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z\"/>\n </svg>\n );\n\n // Fallback content\n const fallbackContent = fallback || (\n displayInitials ? (\n <span className=\"font-medium text-inherit\">\n {displayInitials}\n </span>\n ) : (\n defaultIcon\n )\n );\n\n // Handle click\n const handleClick = React.useCallback(() => {\n if (isClickable && onClick && !isDisabled) {\n onClick();\n }\n }, [isClickable, onClick, isDisabled]);\n\n // Status indicator\n const statusIndicator = showStatus && (\n <Badge\n content=\"\"\n color={isOnline ? 'success' : 'default'}\n shape=\"circle\"\n placement=\"bottom-right\"\n size=\"sm\"\n classNames={{\n badge: isOnline ? 'border-2 border-white' : 'border-2 border-white bg-default-300',\n }}\n />\n );\n\n // Role badge\n const roleBadge = showOrganizationBadge && role && isOrganizationMember && (\n <Badge\n content={getRoleLabel(role)}\n color={getRoleColor(role)}\n variant=\"flat\"\n placement=\"bottom-right\"\n size=\"sm\"\n classNames={{\n badge: 'text-xs px-1 py-0.5 min-w-fit',\n }}\n />\n );\n\n // Base avatar\n const avatar = (\n <Avatar\n src={effectiveSrc}\n name={effectiveName}\n size={size}\n radius={radius}\n color={effectiveColor}\n isBordered={isBordered}\n isDisabled={isDisabled}\n fallback={fallbackContent}\n classNames={{\n base: [\n className,\n isClickable && !isDisabled && 'cursor-pointer hover:opacity-80 transition-opacity',\n isDisabled && 'opacity-50 cursor-not-allowed',\n ].filter(Boolean).join(' '),\n }}\n onClick={handleClick}\n />\n );\n\n // Apply badges\n if (statusIndicator && roleBadge) {\n return (\n <div className=\"relative inline-block\">\n {avatar}\n {statusIndicator}\n <div className=\"absolute -bottom-1 -right-1 transform translate-x-1/2\">\n {roleBadge}\n </div>\n </div>\n );\n }\n\n if (statusIndicator) {\n return (\n <Badge\n content=\"\"\n color={isOnline ? 'success' : 'default'}\n shape=\"circle\"\n placement=\"bottom-right\"\n size=\"sm\"\n classNames={{\n badge: isOnline ? 'border-2 border-white' : 'border-2 border-white bg-default-300',\n }}\n >\n {avatar}\n </Badge>\n );\n }\n\n if (roleBadge) {\n return (\n <Badge\n content={getRoleLabel(role)}\n color={getRoleColor(role)}\n variant=\"flat\"\n placement=\"bottom-right\"\n size=\"sm\"\n classNames={{\n badge: 'text-xs px-1 py-0.5 min-w-fit',\n }}\n >\n {avatar}\n </Badge>\n );\n }\n\n return avatar;\n}\n\n// ============================================================================\n// Export\n// ============================================================================\n\nexport default UserAvatar;"],"names":["generateInitials","name","email","parts","getRoleColor","role","getRoleLabel","UserAvatar","userId","src","size","radius","showStatus","isOnline","fallback","className","onClick","isClickable","showOrganizationBadge","color","isBordered","isDisabled","customInitials","icon","quality","useOrganizationColors","user","isOrganizationMember","useAuth","components","organizationSettings","useConfig","primaryColor","useTheme","CustomUserAvatar","jsx","effectiveUser","effectiveSrc","effectiveName","effectiveEmail","displayInitials","effectiveColor","fallbackContent","handleClick","React","statusIndicator","Badge","roleBadge","avatar","Avatar","jsxs"],"mappings":"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"}