{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "tabs-switcher",
  "type": "registry:ui",
  "title": "Tabs Switcher",
  "description": "A Tabs Switcher component for displaying a tabbed interface.",
  "files": [
    {
      "path": "src/registry/ui/tabs-switcher.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n\nimport { useId, useState } from \"react\";\nimport * as TabsPrimitive from \"@radix-ui/react-tabs\";\nimport { motion, useReducedMotion } from \"motion/react\";\nimport { cn } from \"@/lib/utils\";\n\n// Indicators move on every arrow key, so they settle fast with barely any overshoot.\nconst INDICATOR_SPRING = { type: \"spring\", duration: 0.3, bounce: 0.1 } as const;\n\nexport type TabsSwitcherItem = {\n  value: string;\n  title: React.ReactNode;\n  /** Shown under the title while the item is active. */\n  description?: React.ReactNode;\n  content: React.ReactNode;\n  disabled?: boolean;\n};\n\nexport type TabsSwitcherProps = Omit<\n  React.ComponentProps<typeof TabsPrimitive.Root>,\n  \"children\" | \"activationMode\" | \"dir\"\n> & {\n  items: TabsSwitcherItem[];\n  /** Activate a tab when the pointer hovers its trigger (mouse only). */\n  activateOnHover?: boolean;\n  listClassName?: string;\n  panelClassName?: string;\n};\n\nexport function TabsSwitcher({\n  items,\n  value,\n  defaultValue,\n  onValueChange,\n  activateOnHover = false,\n  orientation = \"vertical\",\n  className,\n  listClassName,\n  panelClassName,\n  ...props\n}: TabsSwitcherProps) {\n  const indicatorId = useId();\n  const reduceMotion = useReducedMotion();\n  const [uncontrolledValue, setUncontrolledValue] = useState(\n    defaultValue ?? items[0]?.value\n  );\n  const activeValue = value ?? uncontrolledValue;\n  const isVertical = orientation === \"vertical\";\n\n  const select = (next: string) => {\n    if (next === activeValue) return;\n    if (value === undefined) setUncontrolledValue(next);\n    onValueChange?.(next);\n  };\n\n  return (\n    <TabsPrimitive.Root\n      data-slot=\"tabs-switcher\"\n      value={activeValue}\n      onValueChange={select}\n      orientation={orientation}\n      className={cn(\n        \"grid gap-6\",\n        isVertical && \"md:grid-cols-[minmax(0,2fr)_minmax(0,3fr)] md:items-start\",\n        className\n      )}\n      {...props}\n    >\n      <TabsPrimitive.List\n        className={cn(\n          \"flex gap-1\",\n          isVertical ? \"flex-col\" : \"flex-col sm:flex-row\",\n          listClassName\n        )}\n      >\n        {items.map((item) => {\n          const isActive = item.value === activeValue;\n          return (\n            <TabsPrimitive.Trigger\n              key={item.value}\n              value={item.value}\n              disabled={item.disabled}\n              onPointerEnter={(event) => {\n                if (activateOnHover && event.pointerType === \"mouse\" && !item.disabled) {\n                  select(item.value);\n                }\n              }}\n              className={cn(\n                \"group relative flex-1 rounded-lg px-4 py-3 text-left outline-none transition-colors\",\n                \"focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50\",\n                \"text-muted-foreground hover:text-foreground data-[state=active]:text-foreground\"\n              )}\n            >\n              {isActive && (\n                <motion.span\n                  layoutId={indicatorId}\n                  aria-hidden\n                  className=\"absolute inset-0 rounded-lg border bg-muted\"\n                  transition={\n                    reduceMotion ? { duration: 0 } : INDICATOR_SPRING\n                  }\n                />\n              )}\n              <span className=\"relative block text-sm font-medium\">{item.title}</span>\n              {item.description && (\n                <span\n                  className={cn(\n                    \"relative grid grid-rows-[0fr] transition-[grid-template-rows] duration-200 ease-[cubic-bezier(0.77,0,0.175,1)] motion-reduce:transition-none\",\n                    \"group-data-[state=active]:grid-rows-[1fr]\"\n                  )}\n                >\n                  <span className=\"overflow-hidden\">\n                    <span className=\"block pt-1 text-sm text-muted-foreground\">\n                      {item.description}\n                    </span>\n                  </span>\n                </span>\n              )}\n            </TabsPrimitive.Trigger>\n          );\n        })}\n      </TabsPrimitive.List>\n\n      {/* Panels share one grid cell so they cross-fade without layout shift; a light blur masks the overlap. */}\n      <div className=\"grid\">\n        {items.map((item) => {\n          const isActive = item.value === activeValue;\n          return (\n            <TabsPrimitive.Content\n              key={item.value}\n              value={item.value}\n              forceMount\n              inert={!isActive}\n              className={cn(\n                \"col-start-1 row-start-1 rounded-xl outline-none transition-[opacity,filter] duration-150 ease-[cubic-bezier(0.23,1,0.32,1)] motion-reduce:transition-opacity\",\n                \"focus-visible:ring-2 focus-visible:ring-ring\",\n                \"data-[state=inactive]:pointer-events-none data-[state=inactive]:opacity-0 data-[state=inactive]:blur-[2px] motion-reduce:data-[state=inactive]:blur-none\",\n                panelClassName\n              )}\n            >\n              {item.content}\n            </TabsPrimitive.Content>\n          );\n        })}\n      </div>\n    </TabsPrimitive.Root>\n  );\n}\n"
    }
  ],
  "dependencies": [
    "@radix-ui/react-tabs",
    "motion"
  ]
}
