{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"tabs","type":"registry:ui","title":"Tabs","description":"Underline tabs. The underline slides to the active tab and resizes to fit its label.","dependencies":["@radix-ui/react-tabs@^1.1.21","motion@^14.0.0"],"files":[{"path":"components/ui/tabs.tsx","type":"registry:ui","content":"\"use client\";\n\nimport * as TabsPrimitive from \"@radix-ui/react-tabs\";\nimport { motion } from \"motion/react\";\nimport * as React from \"react\";\nimport { useControllableState } from \"@/hooks/use-controllable-state\";\nimport { cn } from \"@/lib/utils\";\n\ntype Orientation = \"horizontal\" | \"vertical\";\n\nconst TabsContext = React.createContext<{\n  value: string | undefined;\n  orientation: Orientation;\n  measured: boolean;\n  setMeasured: (measured: boolean) => void;\n}>({\n  value: undefined,\n  orientation: \"horizontal\",\n  measured: false,\n  setMeasured: () => {},\n});\n\nconst indicatorClassName = \"bg-foreground absolute -bottom-px h-[1.5px]\";\nconst highlightClassName = \"bg-foreground/[0.06] dark:bg-foreground/10 absolute -z-10 rounded-md\";\n\nfunction Tabs({\n  className,\n  value: valueProp,\n  defaultValue,\n  onValueChange,\n  orientation = \"horizontal\",\n  ...props\n}: React.ComponentProps<typeof TabsPrimitive.Root>) {\n  const [value, setValue] = useControllableState(valueProp ?? undefined, defaultValue ?? \"\", onValueChange);\n  const [measured, setMeasured] = React.useState(false);\n\n  return (\n    <TabsContext.Provider value={{ value, orientation, measured, setMeasured }}>\n      <TabsPrimitive.Root\n        data-slot=\"tabs\"\n        value={value}\n        onValueChange={setValue}\n        orientation={orientation}\n        className={cn(\"flex gap-2\", orientation === \"horizontal\" && \"flex-col\", className)}\n        {...props}\n      />\n    </TabsContext.Provider>\n  );\n}\n\nfunction TabsList({ className, children, ...props }: React.ComponentProps<typeof TabsPrimitive.List>) {\n  const { value, orientation, setMeasured } = React.useContext(TabsContext);\n  const vertical = orientation === \"vertical\";\n  const list = React.useRef<HTMLDivElement>(null);\n  const [indicator, setIndicator] = React.useState<{\n    orientation: Orientation;\n    box: { x: number; y: number; width: number; height: number };\n  } | null>(null);\n\n  React.useLayoutEffect(() => {\n    const element = list.current;\n    const active = element?.querySelector<HTMLElement>(\"[data-slot=tabs-trigger][data-state=active]\");\n    if (!element || !active) {\n      setIndicator(null);\n      setMeasured(false);\n      return;\n    }\n    const measure = () =>\n      setIndicator({\n        orientation,\n        box: { x: active.offsetLeft, y: active.offsetTop, width: active.offsetWidth, height: active.offsetHeight },\n      });\n    measure();\n    setMeasured(true);\n    const observer = new ResizeObserver(measure);\n    element.querySelectorAll(\"[data-slot=tabs-trigger]\").forEach((trigger) => observer.observe(trigger));\n    return () => observer.disconnect();\n  }, [value, orientation, setMeasured]);\n\n  return (\n    <TabsPrimitive.List\n      ref={list}\n      data-slot=\"tabs-list\"\n      className={cn(\n        vertical\n          ? \"text-muted-foreground relative isolate flex w-full flex-col gap-0.5\"\n          : \"text-muted-foreground relative inline-flex h-10 w-fit items-center justify-start gap-5 border-b\",\n        className,\n      )}\n      {...props}\n    >\n      {children}\n      {indicator && (\n        <motion.span\n          key={indicator.orientation}\n          aria-hidden\n          initial={false}\n          animate={\n            indicator.orientation === \"vertical\" ? indicator.box : { x: indicator.box.x, width: indicator.box.width }\n          }\n          transition={{ type: \"spring\", stiffness: 500, damping: 40 }}\n          className={\n            indicator.orientation === \"vertical\"\n              ? cn(highlightClassName, \"top-0 left-0\")\n              : cn(indicatorClassName, \"left-0\")\n          }\n        />\n      )}\n    </TabsPrimitive.List>\n  );\n}\n\nfunction TabsTrigger({ className, children, value, ...props }: React.ComponentProps<typeof TabsPrimitive.Trigger>) {\n  const { value: activeValue, orientation, measured } = React.useContext(TabsContext);\n  const vertical = orientation === \"vertical\";\n\n  return (\n    <TabsPrimitive.Trigger\n      data-slot=\"tabs-trigger\"\n      value={value}\n      className={cn(\n        \"text-muted-foreground hover:text-foreground data-[state=active]:text-foreground relative inline-flex h-full rounded-md items-center justify-center gap-1.5 px-0.5 text-sm font-medium whitespace-nowrap transition-colors outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-solid focus-visible:outline-foreground disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n        vertical && \"data-[state=inactive]:hover:bg-foreground/[0.03] h-8 justify-start gap-2 px-2.5\",\n        className,\n      )}\n      {...props}\n    >\n      {children}\n      {value === activeValue && !measured && (\n        <span className={vertical ? cn(highlightClassName, \"inset-0\") : cn(indicatorClassName, \"inset-x-0\")} />\n      )}\n    </TabsPrimitive.Trigger>\n  );\n}\n\nfunction TabsContent({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Content>) {\n  return (\n    <TabsPrimitive.Content\n      data-slot=\"tabs-content\"\n      className={cn(\n        \"flex-1 outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-solid focus-visible:outline-foreground\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport { Tabs, TabsContent, TabsList, TabsTrigger };\n"},{"path":"hooks/use-controllable-state.ts","type":"registry:hook","content":"import * as React from \"react\";\n\nexport function useControllableState<T>(prop: T | undefined, defaultProp: T, onChange?: (value: T) => void) {\n  const [uncontrolled, setUncontrolled] = React.useState(defaultProp);\n  const isControlled = prop !== undefined;\n  const value = isControlled ? prop : uncontrolled;\n\n  const setValue = React.useCallback(\n    (next: T) => {\n      if (!isControlled) setUncontrolled(next);\n      onChange?.(next);\n    },\n    [isControlled, onChange],\n  );\n\n  return [value, setValue] as const;\n}\n"}]}