UNPKG

@frank-auth/react

Version:

Flexible and customizable React UI components for Frank Authentication

1 lines 15.7 kB
{"version":3,"file":"user-button.cjs","sources":["../../../../../src/components/auth/user-button/user-button.tsx"],"sourcesContent":["/**\n * @frank-auth/react - User Button Component\n *\n * Main user button component that combines avatar with dropdown menu,\n * providing comprehensive user and organization management interface.\n */\n\n'use client';\n\nimport type React from 'react';\nimport {Button} from '@heroui/react';\nimport {useAuth} from '../../../hooks/use-auth';\nimport {useConfig} from '../../../hooks/use-config';\nimport {UserAvatar, type UserAvatarProps} from './user-avatar';\nimport {UserProfile, type UserProfileMenuItem, type UserProfileProps} from './user-profile';\n\n// ============================================================================\n// User Button Interface\n// ============================================================================\n\nexport interface UserButtonProps {\n /**\n * Button appearance style\n */\n appearance?: 'default' | 'minimal' | 'compact';\n\n /**\n * Avatar props (extends UserAvatarProps)\n */\n avatarProps?: Partial<UserAvatarProps>;\n\n /**\n * Dropdown props (extends UserProfileProps)\n */\n dropdownProps?: Partial<Omit<UserProfileProps, 'children'>>;\n\n /**\n * Custom className for button\n */\n className?: string;\n\n /**\n * Show user name next to avatar\n */\n showName?: boolean;\n\n /**\n * Show organization name\n */\n showOrganization?: boolean;\n\n /**\n * Show online status indicator\n */\n showStatus?: boolean;\n\n /**\n * Show notification badge\n */\n showNotifications?: boolean;\n\n /**\n * Notification count\n */\n notificationCount?: number;\n\n /**\n * Button size\n */\n size?: 'sm' | 'md' | 'lg';\n\n /**\n * Button radius\n */\n radius?: 'none' | 'sm' | 'md' | 'lg' | 'full';\n\n /**\n * Whether button is disabled\n */\n isDisabled?: boolean;\n\n /**\n * Whether button should be full width\n */\n isFullWidth?: boolean;\n\n /**\n * Custom menu items for dropdown\n */\n customMenuItems?: UserProfileMenuItem[];\n\n /**\n * Hide default menu items\n */\n hideMenuItems?: string[];\n\n /**\n * Sign out handler\n */\n onSignOut?: () => void;\n\n /**\n * Profile click handler\n */\n onProfileClick?: () => void;\n\n /**\n * Settings click handler\n */\n onSettingsClick?: () => void;\n\n /**\n * Organization switch handler\n */\n onOrganizationClick?: (organizationId: string) => void;\n\n /**\n * Notification click handler\n */\n onNotificationClick?: () => void;\n\n /**\n * Custom button content (overrides all default content)\n */\n children?: React.ReactNode;\n\n /**\n * Loading state\n */\n isLoading?: boolean;\n\n /**\n * Show dropdown arrow indicator\n */\n showDropdownIndicator?: boolean;\n\n /**\n * Button variant\n */\n variant?: 'solid' | 'bordered' | 'light' | 'flat' | 'faded' | 'shadow' | 'ghost';\n\n /**\n * Button color\n */\n color?: 'default' | 'primary' | 'secondary' | 'success' | 'warning' | 'danger';\n\n /**\n * Custom start content\n */\n startContent?: React.ReactNode;\n\n /**\n * Custom end content\n */\n endContent?: React.ReactNode;\n\n /**\n * Whether to show organization badge on avatar\n */\n showOrganizationBadge?: boolean;\n\n /**\n * User role to display in badge\n */\n role?: string;\n}\n\n// ============================================================================\n// User Button Component\n// ============================================================================\n\nexport function UserButton({\n appearance = 'default',\n avatarProps = {},\n dropdownProps = {},\n className = '',\n showName = false,\n showOrganization = false,\n showStatus = false,\n showNotifications = false,\n notificationCount = 0,\n size = 'md',\n radius = 'lg',\n isDisabled = false,\n isFullWidth = false,\n customMenuItems = [],\n hideMenuItems = [],\n onSignOut,\n onProfileClick,\n onSettingsClick,\n onOrganizationClick,\n onNotificationClick,\n children,\n isLoading = false,\n showDropdownIndicator = false,\n variant = 'light',\n color = 'default',\n startContent,\n endContent,\n showOrganizationBadge = false,\n role,\n }: UserButtonProps) {\n const {\n user,\n isSignedIn,\n isLoading: authLoading,\n userName,\n activeOrganization,\n isOrganizationMember,\n } = useAuth();\n\n const { components } = useConfig();\n\n // Custom component override\n const CustomUserButton = components.UserButton;\n if (CustomUserButton) {\n return <CustomUserButton {...{\n appearance, avatarProps, dropdownProps, className, showName, showOrganization,\n showStatus, showNotifications, notificationCount, size, radius, isDisabled,\n isFullWidth, customMenuItems, hideMenuItems, onSignOut, onProfileClick,\n onSettingsClick, onOrganizationClick, onNotificationClick, children,\n isLoading, showDropdownIndicator, variant, color, startContent, endContent,\n showOrganizationBadge, role\n }} />;\n }\n\n // Don't render if user is not signed in\n if (!isSignedIn || !user) {\n return null;\n }\n\n // Effective loading state\n const effectiveIsLoading = isLoading || authLoading;\n\n // Avatar size mapping\n const avatarSizeMap = {\n sm: 'sm' as const,\n md: 'md' as const,\n lg: 'lg' as const,\n };\n\n // Get effective role for badge\n const effectiveRole = role || (isOrganizationMember ? 'member' : undefined);\n\n // Dropdown indicator\n const dropdownIndicator = showDropdownIndicator && (\n <svg className=\"w-4 h-4 text-default-400\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M19 9l-7 7-7-7\" />\n </svg>\n );\n\n // Notification badge\n const notificationBadge = showNotifications && notificationCount > 0 && (\n <div className=\"absolute -top-1 -right-1 bg-danger text-white text-xs rounded-full min-w-[1.25rem] h-5 flex items-center justify-center\">\n {notificationCount > 99 ? '99+' : notificationCount}\n </div>\n );\n\n // Render different appearances\n const renderButtonContent = () => {\n // Custom children override\n if (children) {\n return children;\n }\n\n // Avatar component\n const avatar = (\n <div className=\"relative\">\n <UserAvatar\n size={avatarSizeMap[size]}\n showStatus={showStatus}\n showOrganizationBadge={showOrganizationBadge}\n role={effectiveRole}\n isClickable={false}\n {...avatarProps}\n />\n {notificationBadge}\n </div>\n );\n\n switch (appearance) {\n case 'minimal':\n return (\n <div className=\"flex items-center gap-0\">\n {startContent}\n {avatar}\n {endContent || dropdownIndicator}\n </div>\n );\n\n case 'compact':\n return (\n <div className=\"flex items-center gap-2\">\n {startContent}\n {avatar}\n {(showName || showOrganization) && (\n <div className=\"flex flex-col items-start min-w-0\">\n {showName && userName && (\n <span className=\"text-sm font-medium text-default-700 truncate max-w-[120px]\">\n {userName}\n </span>\n )}\n {showOrganization && activeOrganization && (\n <span className=\"text-xs text-default-500 truncate max-w-[120px]\">\n {activeOrganization.name}\n </span>\n )}\n </div>\n )}\n {endContent || dropdownIndicator}\n </div>\n );\n\n default:\n return (\n <div className=\"flex items-center gap-3\">\n {startContent}\n {avatar}\n {(showName || showOrganization) && (\n <div className=\"flex flex-col items-start min-w-0 flex-1\">\n {showName && userName && (\n <span className=\"text-sm font-medium text-default-700 truncate\">\n {userName}\n </span>\n )}\n {showOrganization && activeOrganization && (\n <span className=\"text-xs text-default-500 truncate\">\n {activeOrganization.name}\n </span>\n )}\n </div>\n )}\n {endContent || dropdownIndicator}\n </div>\n );\n }\n };\n\n // Base button\n const button = (\n <Button\n size={size}\n radius={radius}\n variant={variant}\n color={color}\n isDisabled={isDisabled || effectiveIsLoading}\n isLoading={effectiveIsLoading}\n fullWidth={isFullWidth}\n className={[\n 'justify-start h-auto',\n appearance === 'minimal' ? 'min-w-unit-10 p-1' :\n appearance === 'compact' ? 'px-2 py-1.5' : 'px-3 py-2',\n className\n ].filter(Boolean).join(' ')}\n >\n {renderButtonContent()}\n </Button>\n );\n\n // Wrap with dropdown\n return (\n <UserProfile\n customItems={customMenuItems}\n hideDefaultItems={hideMenuItems}\n onSignOut={onSignOut}\n onProfileClick={onProfileClick}\n onSettingsClick={onSettingsClick}\n onOrganizationSwitch={onOrganizationClick}\n isDisabled={isDisabled || effectiveIsLoading}\n {...dropdownProps}\n >\n {button}\n </UserProfile>\n );\n}\n\n// ============================================================================\n// Export\n// ============================================================================\n\nexport default UserButton;"],"names":["UserButton","appearance","avatarProps","dropdownProps","className","showName","showOrganization","showStatus","showNotifications","notificationCount","size","radius","isDisabled","isFullWidth","customMenuItems","hideMenuItems","onSignOut","onProfileClick","onSettingsClick","onOrganizationClick","onNotificationClick","children","isLoading","showDropdownIndicator","variant","color","startContent","endContent","showOrganizationBadge","role","user","isSignedIn","authLoading","userName","activeOrganization","isOrganizationMember","useAuth","components","useConfig","CustomUserButton","jsx","effectiveIsLoading","avatarSizeMap","effectiveRole","dropdownIndicator","notificationBadge","renderButtonContent","avatar","jsxs","UserAvatar","button","Button","UserProfile"],"mappings":"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"}