{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "profile-menu",
  "title": "Profile Menu",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-svelte/blocks/profile-menu/ProfileMenu.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { Snippet } from 'svelte'\n\n  export interface ProfileMenuUser {\n    name: string\n    email: string\n    avatar?: string\n  }\n\n  export type ProfileMenuKey = 'upgrade' | 'account' | 'billing' | 'notifications' | 'settings' | 'logout'\n\n  export interface ProfileMenuProps {\n    user?: ProfileMenuUser\n    /** Custom trigger. Receives the user, the computed initials, and the\n     *  trigger props to spread onto your own element (Svelte has no automatic\n     *  as-child merge, so spreading `props` is what wires the toggle). */\n    children?: Snippet<[{ user: ProfileMenuUser; initials: string; props: Record<string, unknown> }]>\n    onselect?: (key: ProfileMenuKey) => void\n  }\n</script>\n\n<script lang=\"ts\">\n  import { BadgeCheck, Bell, CreditCard, LogOut, Settings, Zap } from '@lucide/svelte'\n  import { Avatar, AvatarFallback, AvatarImage } from '$lib/components/ui/avatar'\n  import {\n    DropdownMenu,\n    DropdownMenuContent,\n    DropdownMenuGroup,\n    DropdownMenuItem,\n    DropdownMenuLabel,\n    DropdownMenuSeparator,\n    DropdownMenuTrigger,\n  } from '$lib/components/ui/dropdown-menu'\n\n  let {\n    user = { name: 'Alex Morgan', email: 'alex@example.com', avatar: '' },\n    children,\n    onselect,\n  }: ProfileMenuProps = $props()\n\n  const initials = $derived.by(() => {\n    const parts = user.name.trim().split(/\\s+/).slice(0, 2)\n    return parts.map((p) => p[0]?.toUpperCase()).join('') || 'U'\n  })\n</script>\n\n<!-- DropdownMenu renders no DOM of its own, so the slot marker lives on a\n     display:contents wrapper that vanishes from layout. -->\n<div data-slot=\"profile-menu\" class=\"contents\">\n  <DropdownMenu>\n    {#if children}\n      <DropdownMenuTrigger>\n        {#snippet child({ props })}\n          {@render children({ user, initials, props })}\n        {/snippet}\n      </DropdownMenuTrigger>\n    {:else}\n      <DropdownMenuTrigger\n        class=\"hover:bg-accent focus-visible:ring-ring flex size-8 items-center justify-center rounded-lg transition-colors focus-visible:ring-1 focus-visible:outline-none\"\n        aria-label=\"Open profile menu\"\n      >\n        <Avatar class=\"size-7\">\n          {#if user.avatar}\n            <AvatarImage src={user.avatar} alt={user.name} />\n          {/if}\n          <AvatarFallback class=\"bg-primary/10 text-primary text-xs font-semibold\">\n            {initials}\n          </AvatarFallback>\n        </Avatar>\n      </DropdownMenuTrigger>\n    {/if}\n    <DropdownMenuContent class=\"w-60 rounded-lg\" align=\"end\" sideOffset={8}>\n      <DropdownMenuLabel class=\"p-0 font-normal\">\n        <div class=\"flex items-center gap-2 px-1 py-1.5 text-left text-sm\">\n          <Avatar class=\"size-9 rounded-lg\">\n            {#if user.avatar}\n              <AvatarImage src={user.avatar} alt={user.name} />\n            {/if}\n            <AvatarFallback class=\"bg-primary/10 text-primary rounded-lg text-xs font-semibold\">\n              {initials}\n            </AvatarFallback>\n          </Avatar>\n          <div class=\"grid flex-1 text-left leading-tight\">\n            <span class=\"truncate text-sm font-semibold\">{user.name}</span>\n            <span class=\"text-muted-foreground truncate text-xs\">{user.email}</span>\n          </div>\n        </div>\n      </DropdownMenuLabel>\n      <DropdownMenuSeparator />\n      <DropdownMenuGroup>\n        <DropdownMenuItem onSelect={() => onselect?.('upgrade')}>\n          <Zap class=\"size-4\" />\n          Upgrade to Pro\n        </DropdownMenuItem>\n      </DropdownMenuGroup>\n      <DropdownMenuSeparator />\n      <DropdownMenuGroup>\n        <DropdownMenuItem onSelect={() => onselect?.('account')}>\n          <BadgeCheck class=\"size-4\" />\n          Account\n        </DropdownMenuItem>\n        <DropdownMenuItem onSelect={() => onselect?.('billing')}>\n          <CreditCard class=\"size-4\" />\n          Billing\n        </DropdownMenuItem>\n        <DropdownMenuItem onSelect={() => onselect?.('notifications')}>\n          <Bell class=\"size-4\" />\n          Notifications\n        </DropdownMenuItem>\n        <DropdownMenuItem onSelect={() => onselect?.('settings')}>\n          <Settings class=\"size-4\" />\n          Settings\n        </DropdownMenuItem>\n      </DropdownMenuGroup>\n      <DropdownMenuSeparator />\n      <DropdownMenuItem onSelect={() => onselect?.('logout')}>\n        <LogOut class=\"size-4\" />\n        Log out\n      </DropdownMenuItem>\n    </DropdownMenuContent>\n  </DropdownMenu>\n</div>\n",
      "type": "registry:block",
      "target": "~/src/lib/components/blocks/profile-menu/ProfileMenu.svelte"
    }
  ],
  "dependencies": [
    "@lucide/svelte"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/svelte/avatar.json",
    "https://uipkge.dev/r/svelte/dropdown-menu.json"
  ],
  "description": "Header-grade profile/account dropdown. Child-snippet DropdownMenu the consumer wraps around their own avatar button (default trigger ships an Avatar fallback). Sections: upgrade, account/billing/notifications/settings, log out. Calls `onselect` with the chosen item key so consumers wire navigation and auth themselves.",
  "categories": [
    "layout",
    "dashboard",
    "overlay"
  ]
}