{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "user-menu",
  "title": "User Menu",
  "description": "An account menu with organisation switching, theme toggle and sign-out, shown in the app shell.",
  "dependencies": [
    "@untitledui/icons@^0.0.22"
  ],
  "registryDependencies": [
    "@nx-ui/avatar",
    "@nx-ui/dropdown-menu",
    "@nx-ui/org-switcher",
    "@nx-ui/theme-toggle",
    "@nx-ui/utils"
  ],
  "files": [
    {
      "path": "components/blocks/user-menu.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Check, ChevronSelectorVertical, LogOut01 } from \"@untitledui/icons\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\"\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuGroup,\n  DropdownMenuItem,\n  DropdownMenuLabel,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\"\nimport { OrgAvatar, type Org } from \"@/components/blocks/org-switcher\"\nimport { ThemeToggleMenuItems } from \"@/components/blocks/theme-toggle\"\n\nexport type User = {\n  name: string\n  email: string\n  avatarUrl?: string\n}\n\nfunction initialsOf(name: string) {\n  return name\n    .split(/\\s+/)\n    .filter(Boolean)\n    .slice(0, 2)\n    .map((word) => word[0]?.toUpperCase() ?? \"\")\n    .join(\"\")\n}\n\nfunction UserAvatar({\n  user,\n  size = \"default\",\n}: {\n  user: User\n  size?: \"sm\" | \"default\" | \"lg\"\n}) {\n  return (\n    <Avatar size={size}>\n      {user.avatarUrl ? <AvatarImage src={user.avatarUrl} alt={user.name} /> : null}\n      <AvatarFallback>{initialsOf(user.name)}</AvatarFallback>\n    </Avatar>\n  )\n}\n\n/**\n * The UUI account-menu section divider: an inset, rounded-off hairline with\n * breathing room, rather than the default edge-to-edge menu separator — so\n * the menu reads as softly separated blocks.\n */\nfunction SectionDivider() {\n  return (\n    <DropdownMenuSeparator className=\"mx-1 my-1.5 rounded-full bg-border-secondary\" />\n  )\n}\n\n/**\n * UserMenu — the nx account card. The trigger is a compact card (avatar, name\n * and a truncated secondary line, plus a chevron); opening it reveals a header\n * card (avatar + name + email as a subtly-inset rounded block, per the UUI\n * account-menu treatment), any custom items passed as `children`, an optional\n * organisation section, a theme light/dark/system control, and a sign-out\n * action — the sections separated by inset rounded dividers.\n *\n * `children` are rendered as-is between the header and the organisation/theme\n * sections — pass `DropdownMenuItem`s (and `DropdownMenuSeparator`s) from\n * `@/components/ui/dropdown-menu` for profile/settings/billing links.\n *\n * Organisation selection is additive: pass `orgs` (the `Org` shape shared with\n * OrgSwitcher) to render an \"Organisations\" section inside the dropdown, with\n * a tick on `activeOrgId` and changes leaving via `onOrgChange` — mirroring\n * Untitled UI's account-menu \"switch account\" pattern. Omit `orgs` and the\n * menu is unchanged.\n */\nfunction UserMenu({\n  user,\n  children,\n  onSignOut,\n  secondaryLabel,\n  orgs,\n  activeOrgId,\n  onOrgChange,\n  className,\n}: {\n  user: User\n  children?: React.ReactNode\n  onSignOut?: () => void\n  /** Overrides the trigger's second line (defaults to the email). */\n  secondaryLabel?: React.ReactNode\n  /** Organisations to offer in the menu's org-selection section. */\n  orgs?: Org[]\n  /** The org that carries the active tick. */\n  activeOrgId?: string\n  /** Called with the selected org id — treat as an auth-scope change. */\n  onOrgChange?: (orgId: string) => void\n  className?: string\n}) {\n  return (\n    <DropdownMenu>\n      <DropdownMenuTrigger\n        className={cn(\n          \"flex w-full items-center gap-3 rounded-lg bg-background p-3 text-left ring-1 ring-border ring-inset outline-none transition hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring\",\n          className\n        )}\n        aria-label=\"Open account menu\"\n      >\n        <UserAvatar user={user} />\n        <span className=\"flex min-w-0 flex-1 flex-col\">\n          <span className=\"truncate text-sm font-semibold text-foreground\">\n            {user.name}\n          </span>\n          <span className=\"truncate text-xs text-muted-foreground\">\n            {secondaryLabel ?? user.email}\n          </span>\n        </span>\n        <ChevronSelectorVertical className=\"size-4 shrink-0 text-fg-quaternary\" />\n      </DropdownMenuTrigger>\n\n      <DropdownMenuContent\n        className=\"min-w-64 rounded-xl p-1.5\"\n        align=\"end\"\n        side=\"top\"\n      >\n        <DropdownMenuGroup>\n          <DropdownMenuLabel className=\"flex items-center gap-2.5 rounded-lg bg-bg-secondary_alt p-2\">\n            <UserAvatar user={user} size=\"sm\" />\n            <span className=\"flex min-w-0 flex-1 flex-col\">\n              <span className=\"truncate text-sm font-semibold text-foreground\">\n                {user.name}\n              </span>\n              <span className=\"truncate text-xs font-normal text-muted-foreground\">\n                {user.email}\n              </span>\n            </span>\n          </DropdownMenuLabel>\n        </DropdownMenuGroup>\n\n        {children ? (\n          <>\n            <SectionDivider />\n            {children}\n          </>\n        ) : null}\n\n        {orgs && orgs.length > 0 ? (\n          <>\n            <SectionDivider />\n            <DropdownMenuGroup>\n              <DropdownMenuLabel>Organisations</DropdownMenuLabel>\n              {orgs.map((org) => {\n                const isActive = org.id === activeOrgId\n                return (\n                  <DropdownMenuItem\n                    key={org.id}\n                    onClick={() => onOrgChange?.(org.id)}\n                    className={cn(\"gap-2.5\", isActive && \"bg-accent\")}\n                    data-active-org={isActive || undefined}\n                  >\n                    <OrgAvatar org={org} size=\"sm\" />\n                    <span className=\"flex min-w-0 flex-1 flex-col\">\n                      <span className=\"truncate text-sm font-semibold text-secondary-foreground\">\n                        {org.name}\n                      </span>\n                      {org.meta ? (\n                        <span className=\"truncate text-xs font-normal text-muted-foreground\">\n                          {org.meta}\n                        </span>\n                      ) : null}\n                    </span>\n                    {isActive ? (\n                      <Check\n                        className=\"size-4 shrink-0 text-fg-brand-primary\"\n                        aria-label=\"Active organisation\"\n                      />\n                    ) : null}\n                  </DropdownMenuItem>\n                )\n              })}\n            </DropdownMenuGroup>\n          </>\n        ) : null}\n\n        <SectionDivider />\n        <ThemeToggleMenuItems />\n\n        {onSignOut ? (\n          <>\n            <SectionDivider />\n            <DropdownMenuItem onClick={onSignOut}>\n              <LogOut01 className=\"size-5 text-fg-quaternary\" />\n              <span className=\"text-sm font-semibold text-secondary-foreground\">\n                Sign out\n              </span>\n            </DropdownMenuItem>\n          </>\n        ) : null}\n      </DropdownMenuContent>\n    </DropdownMenu>\n  )\n}\n\nexport { UserMenu, UserAvatar }\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:block"
}