Skip to content

UserDropDown (Component)

Description

The UserDropDown component is a modern and elegant dropdown menu designed to display user information and provide quick access to main actions. It integrates perfectly into a navbar or application header.

Preview

Button Co -->mponent Variants
*Modern dropdown menu with user avatar, name display, and customizable menu items*

Features

  • 🎨 Modern design with smooth animations
  • 👤 User display with avatar and information
  • 🔧 Customizable menu with default items
  • 📱 Responsive with mobile adaptation
  • Composable API for easy integration
  • 🎯 Accessibility with focus and keyboard navigation

Usage

Import

vue
<template>
  <UserDropDown 
    :user="currentUser"
    :menu-items="customMenuItems"
  />
</template>

Props

PropTypeDefaultDescription
userObject | nullnullUser object with metadata
menuItemsDropdownItem[][]List of menu items

User Object Structure

typescript
interface User {
  email: string
  user_metadata: {
    full_name?: string
    avatar_url?: string
  }
}
typescript
interface DropdownItem {
  id: string
  label: string
  icon?: string
  href?: string
  action?: () => void
  divider?: boolean
  disabled?: boolean
}

Examples

Basic Usage

vue
<template>
  <UserDropDown />
</template>

With Custom User

vue
<template>
  <UserDropDown 
    :user="{
      email: 'john@example.com',
      user_metadata: {
        full_name: 'John Doe',
        avatar_url: 'https://example.com/avatar.jpg'
      }
    }"
  />
</template>

Custom Menu

vue
<template>
  <UserDropDown 
    :menu-items="customMenuItems"
  />
</template>

<script setup>
const customMenuItems = [
  {
    id: 'dashboard',
    label: 'Dashboard',
    icon: 'heroicons:squares-2x2',
    href: '/dashboard'
  },
  {
    id: 'projects',
    label: 'My Projects',
    icon: 'heroicons:folder',
    href: '/projects'
  },
  {
    id: 'divider-1',
    divider: true
  },
  {
    id: 'settings',
    label: 'Settings',
    icon: 'heroicons:cog-6-tooth',
    href: '/settings'
  },
  {
    id: 'logout',
    label: 'Sign Out',
    icon: 'heroicons:arrow-right-on-rectangle',
    action: () => {
      // Logout logic
      console.log('Signing out...')
    }
  }
]
</script>
vue
<template>
  <nav class="flex items-center justify-between p-4 bg-white shadow-sm">
    <div class="flex items-center space-x-4">
      <Logo />
      <NavigationMenu />
    </div>
    
    <div class="flex items-center space-x-4">
      <NotificationBell />
      <UserDropDown />
    </div>
  </nav>
</template>

Composable API

The component uses the useDropdown composable to manage the menu state:

typescript
const { isOpen, dropdownRef, toggle, close } = useDropdown()

Available Methods

  • toggle() : Opens/closes the menu
  • close() : Closes the menu
  • isOpen : Reactive opening state

Default Menu

If no menuItems are provided, the component uses a default menu:

typescript
const defaultMenuItems = [
  {
    id: 'profile',
    label: 'My Profile',
    icon: 'heroicons:user',
    href: '/profile'
  },
  {
    id: 'settings',
    label: 'Settings',
    icon: 'heroicons:cog-6-tooth',
    href: '/settings'
  },
  {
    id: 'divider-1',
    divider: true
  },
  {
    id: 'logout',
    label: 'Sign Out',
    icon: 'heroicons:arrow-right-on-rectangle',
    action: () => {}
  }
]

Styles and Themes

The component uses Tailwind CSS classes and automatically adapts to the application theme:

Main Classes

  • bg-popover : Menu background
  • text-popover-foreground : Text color
  • border : Menu border
  • shadow-md : Drop shadow
  • bg-primary : Avatar color
  • text-primary-foreground : Avatar text color

Responsive

  • User name is hidden on mobile (hidden md:block)
  • Menu adapts to screen width
  • Automatic positioning to avoid overflow

Animations

The component includes smooth animations:

  • Entry : Scale + fade + translate
  • Exit : Scale + fade + reverse translate
  • Chevron icon : 180° rotation on opening

Accessibility

  • Keyboard focus support
  • Navigation with Tab/Shift+Tab
  • Close with Escape
  • Close by clicking outside
  • Appropriate ARIA attributes

Supabase Integration

The component automatically integrates with Supabase Auth:

vue
<script setup>
// Automatic retrieval of connected user
const user = computed(() => {
  return props.user || useSupabaseUser().value
})
</script>

Advanced Customization

Custom Styles

vue
<template>
  <UserDropDown 
    class="custom-user-dropdown"
    :menu-items="menuItems"
  />
</template>

<style>
.custom-user-dropdown {
  /* Custom styles */
}
</style>

Custom Actions

vue
<script setup>
const handleLogout = async () => {
  const supabase = useSupabaseClient()
  await supabase.auth.signOut()
  await navigateTo('/login')
}

const menuItems = [
  // ... other items
  {
    id: 'logout',
    label: 'Sign Out',
    icon: 'heroicons:arrow-right-on-rectangle',
    action: handleLogout
  }
]
</script>

Best Practices

  1. Always provide a fallback for non-connected users
  2. Use consistent icons with the design system
  3. Group actions with logical separators
  4. Test accessibility with keyboard navigation
  5. Optimize performance by avoiding unnecessary re-renders

Troubleshooting

  • Check that the useDropdown composable is properly imported
  • Ensure the @click event is properly attached

Styles don't apply

  • Verify that Tailwind CSS is configured
  • Make sure custom CSS classes are defined

User doesn't appear

  • Check the user object structure
  • Ensure Supabase Auth is configured

Complete Example

vue
<template>
  <div class="min-h-screen bg-gray-50">
    <header class="bg-white shadow-sm">
      <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
        <div class="flex justify-between items-center h-16">
          <div class="flex items-center">
            <h1 class="text-xl font-bold">My App</h1>
          </div>
          
          <div class="flex items-center space-x-4">
            <button class="p-2 text-gray-400 hover:text-gray-500">
              <Icon name="heroicons:bell" class="w-5 h-5" />
            </button>
            
            <UserDropDown 
              :menu-items="userMenuItems"
            />
          </div>
        </div>
      </div>
    </header>
    
    <main class="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
      <!-- Main content -->
    </main>
  </div>
</template>

<script setup>
const userMenuItems = [
  {
    id: 'profile',
    label: 'My Profile',
    icon: 'heroicons:user',
    href: '/profile'
  },
  {
    id: 'settings',
    label: 'Settings',
    icon: 'heroicons:cog-6-tooth',
    href: '/settings'
  },
  {
    id: 'billing',
    label: 'Billing',
    icon: 'heroicons:credit-card',
    href: '/billing'
  },
  {
    id: 'divider-1',
    divider: true
  },
  {
    id: 'help',
    label: 'Help',
    icon: 'heroicons:question-mark-circle',
    href: '/help'
  },
  {
    id: 'logout',
    label: 'Sign Out',
    icon: 'heroicons:arrow-right-on-rectangle',
    action: async () => {
      const supabase = useSupabaseClient()
      await supabase.auth.signOut()
      await navigateTo('/login')
    }
  }
]
</script>

This component is part of the ClawPlate suite and is optimized for modern SaaS applications.

Built with love by mhdevfr