{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "team-switcher",
  "title": "Team Switcher",
  "description": "Switch between teams and workspaces with search.",
  "registryDependencies": [
    "avatar",
    "badge",
    "button",
    "command-menu",
    "popover"
  ],
  "files": [
    {
      "path": "registry/new-york/blocks/team/team-switcher.tsx",
      "content": "\"use client\";\n\nimport { Check, ChevronsUpDown, Plus } from \"lucide-react\";\nimport { useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport {\n  Avatar,\n  AvatarFallback,\n  AvatarImage,\n} from \"@/registry/new-york/ui/avatar\";\nimport { Badge } from \"@/registry/new-york/ui/badge\";\nimport { Button } from \"@/registry/new-york/ui/button\";\nimport {\n  CommandMenuEmpty,\n  CommandMenuInput,\n  CommandMenuItem,\n  CommandMenuList,\n  CommandMenuProvider,\n  CommandMenuSeparator,\n} from \"@/registry/new-york/ui/command-menu\";\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from \"@/registry/new-york/ui/popover\";\n\nexport interface Team {\n  id: string;\n  name: string;\n  avatar?: string;\n  plan?: \"free\" | \"pro\" | \"enterprise\";\n  memberCount?: number;\n  isPersonal?: boolean;\n}\n\nexport interface TeamSwitcherProps {\n  teams?: Team[];\n  currentTeamId?: string;\n  onTeamSelect?: (teamId: string) => void;\n  onCreateTeam?: () => void;\n  className?: string;\n  showPlan?: boolean;\n}\n\nfunction getInitials(name: string): string {\n  return name\n    .split(\" \")\n    .map((n) => n[0])\n    .join(\"\")\n    .toUpperCase()\n    .slice(0, 2);\n}\n\nfunction getPlanBadgeVariant(plan?: Team[\"plan\"]) {\n  switch (plan) {\n    case \"enterprise\":\n      return \"default\";\n    case \"pro\":\n      return \"secondary\";\n    default:\n      return \"outline\";\n  }\n}\n\nexport default function TeamSwitcher({\n  teams = [],\n  currentTeamId,\n  onTeamSelect,\n  onCreateTeam,\n  className,\n  showPlan = true,\n}: TeamSwitcherProps) {\n  const [open, setOpen] = useState(false);\n  const [searchValue, setSearchValue] = useState(\"\");\n  const [selectedIndex, setSelectedIndex] = useState(0);\n\n  const currentTeam = teams.find((t) => t.id === currentTeamId) || teams[0];\n  const otherTeams = teams.filter((t) => t.id !== currentTeamId);\n\n  const filteredTeams = otherTeams.filter((team) => {\n    if (!searchValue.trim()) return true;\n    const query = searchValue.toLowerCase();\n    return team.name.toLowerCase().includes(query);\n  });\n\n  const handleSelect = (teamId: string) => {\n    onTeamSelect?.(teamId);\n    setOpen(false);\n    setSearchValue(\"\");\n  };\n\n  return (\n    <Popover onOpenChange={setOpen} open={open}>\n      <PopoverTrigger asChild>\n        <Button\n          aria-label=\"Switch team\"\n          className={cn(\"justify-between gap-2\", className)}\n          type=\"button\"\n          variant=\"outline\"\n        >\n          <div className=\"flex items-center gap-2\">\n            <Avatar className=\"size-5\">\n              <AvatarImage alt={currentTeam?.name} src={currentTeam?.avatar} />\n              <AvatarFallback className=\"text-xs\">\n                {currentTeam ? getInitials(currentTeam.name) : \"T\"}\n              </AvatarFallback>\n            </Avatar>\n            <span className=\"font-medium\">\n              {currentTeam?.name || \"Select team\"}\n            </span>\n            {showPlan && currentTeam?.plan && (\n              <Badge\n                className=\"text-xs\"\n                variant={getPlanBadgeVariant(currentTeam.plan)}\n              >\n                {currentTeam.plan}\n              </Badge>\n            )}\n          </div>\n          <ChevronsUpDown className=\"size-4 text-muted-foreground\" />\n        </Button>\n      </PopoverTrigger>\n      <PopoverContent align=\"start\" className=\"w-[280px] p-0\">\n        <CommandMenuProvider\n          scrollHideDelay={600}\n          scrollType=\"hover\"\n          selectedIndex={selectedIndex}\n          setSelectedIndex={setSelectedIndex}\n          setValue={setSearchValue}\n          value={searchValue}\n        >\n          <CommandMenuInput placeholder=\"Search teams…\" />\n          <CommandMenuList>\n            {currentTeam && (\n              <>\n                <CommandMenuItem\n                  className=\"rounded\"\n                  index={0}\n                  onSelect={() => handleSelect(currentTeam.id)}\n                >\n                  <div className=\"flex items-center gap-2\">\n                    <Avatar className=\"size-6\">\n                      <AvatarImage\n                        alt={currentTeam.name}\n                        src={currentTeam.avatar}\n                      />\n                      <AvatarFallback className=\"text-xs\">\n                        {getInitials(currentTeam.name)}\n                      </AvatarFallback>\n                    </Avatar>\n                    <div className=\"flex min-w-0 flex-1 flex-col\">\n                      <span className=\"font-medium text-sm\">\n                        {currentTeam.name}\n                      </span>\n                      {currentTeam.memberCount !== undefined && (\n                        <span className=\"text-muted-foreground text-xs\">\n                          {currentTeam.memberCount} member\n                          {currentTeam.memberCount !== 1 ? \"s\" : \"\"}\n                        </span>\n                      )}\n                    </div>\n                    <Check className=\"size-4 text-primary\" />\n                  </div>\n                </CommandMenuItem>\n                {otherTeams.length > 0 && <CommandMenuSeparator />}\n              </>\n            )}\n            {filteredTeams.length === 0 ? (\n              <CommandMenuEmpty>No teams found</CommandMenuEmpty>\n            ) : (\n              filteredTeams.map((team, index) => (\n                <CommandMenuItem\n                  className=\"rounded\"\n                  index={index}\n                  key={team.id}\n                  onSelect={() => handleSelect(team.id)}\n                >\n                  <div className=\"flex items-center gap-2\">\n                    <Avatar className=\"size-6\">\n                      <AvatarImage alt={team.name} src={team.avatar} />\n                      <AvatarFallback className=\"text-xs\">\n                        {getInitials(team.name)}\n                      </AvatarFallback>\n                    </Avatar>\n                    <div className=\"flex min-w-0 flex-1 flex-col\">\n                      <span className=\"font-medium text-sm\">{team.name}</span>\n                      {team.memberCount !== undefined && (\n                        <span className=\"text-muted-foreground text-xs\">\n                          {team.memberCount} member\n                          {team.memberCount !== 1 ? \"s\" : \"\"}\n                        </span>\n                      )}\n                    </div>\n                    {showPlan && team.plan && (\n                      <Badge\n                        className=\"text-xs\"\n                        variant={getPlanBadgeVariant(team.plan)}\n                      >\n                        {team.plan}\n                      </Badge>\n                    )}\n                  </div>\n                </CommandMenuItem>\n              ))\n            )}\n            {onCreateTeam && (\n              <>\n                <CommandMenuSeparator />\n                <CommandMenuItem\n                  icon={<Plus className=\"size-4\" />}\n                  index={filteredTeams.length}\n                  onSelect={onCreateTeam}\n                >\n                  Create new team\n                </CommandMenuItem>\n              </>\n            )}\n          </CommandMenuList>\n        </CommandMenuProvider>\n      </PopoverContent>\n    </Popover>\n  );\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "block",
    "team"
  ],
  "type": "registry:ui"
}