{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "tabs",
  "title": "Tabs",
  "description": "Segmented control that swaps one panel for another in place, so related views share a single spot on the screen.",
  "dependencies": [
    "radix-ui",
    "motion"
  ],
  "registryDependencies": [
    "utils",
    "https://miseui.junsu4621.workers.dev/r/motion.json"
  ],
  "files": [
    {
      "path": "registry/miseui/ui/tabs.tsx",
      "content": "import type * as React from \"react\"\nimport { createContext, useContext, useId, useMemo, useState } from \"react\"\nimport { Tabs as TabsPrimitive } from \"radix-ui\"\nimport { LayoutGroup, motion, useReducedMotion } from \"motion/react\"\nimport { SPRING_DEFAULT } from \"@/registry/miseui/lib/motion\"\nimport { cn } from \"@/registry/miseui/lib/utils\"\n\ntype TabsEffect = \"spring\" | \"pill\"\n\ninterface TabsEffectValue {\n  effect: TabsEffect\n  value: string | undefined\n  reduceMotion: boolean\n}\n\nconst TabsEffectContext = createContext<TabsEffectValue | null>(null)\n\n/** Motion corrects a corner radius for layout scaling only when it is a px value in `style`, so the pill stays round mid-slide. */\nconst PILL_RADIUS_PX = 999\n\nconst EFFECT_LIST_CLASS: Record<TabsEffect, string> = {\n  spring: \"h-10 gap-1 rounded-none border-b border-border bg-transparent p-0\",\n  pill: \"h-10 rounded-full\",\n}\n\nconst EFFECT_TRIGGER_CLASS: Record<TabsEffect, string> = {\n  spring: \"px-3 data-[state=active]:bg-transparent data-[state=active]:shadow-none\",\n  pill: \"rounded-full px-3 data-[state=active]:bg-transparent data-[state=active]:text-mise-foreground data-[state=active]:shadow-none\",\n}\n\nconst INDICATOR_CLASS: Record<TabsEffect, string> = {\n  spring: \"absolute inset-x-1 -bottom-0.5 -z-10 h-0.5 rounded-full bg-mise\",\n  pill: \"absolute -inset-px -z-10 bg-mise shadow-sm\",\n}\n\ntype TabsProps = React.ComponentProps<typeof TabsPrimitive.Root> & {\n  /** `spring` slides an underline between triggers; `pill` slides a filled pill behind the label. */\n  effect?: TabsEffect\n}\n\nfunction Tabs({ effect, className, ...props }: TabsProps) {\n  if (effect) return <EffectTabs effect={effect} className={className} {...props} />\n  return <TabsPrimitive.Root data-slot=\"tabs\" className={cn(\"flex flex-col gap-2\", className)} {...props} />\n}\n\nfunction EffectTabs({\n  effect,\n  className,\n  value,\n  defaultValue,\n  onValueChange,\n  ...props\n}: TabsProps & { effect: TabsEffect }) {\n  // layoutId is global to the page; without a per-instance group two Tabs would fight over one indicator.\n  const groupId = useId()\n  const reduceMotion = useReducedMotion() ?? false\n  const [uncontrolled, setUncontrolled] = useState(defaultValue)\n  const current = value ?? uncontrolled\n  const context = useMemo(() => ({ effect, value: current, reduceMotion }), [effect, current, reduceMotion])\n\n  return (\n    <TabsEffectContext.Provider value={context}>\n      <LayoutGroup id={groupId}>\n        <TabsPrimitive.Root\n          data-slot=\"tabs\"\n          data-effect={effect}\n          className={cn(\"flex flex-col gap-2\", className)}\n          value={value}\n          defaultValue={defaultValue}\n          onValueChange={(next) => {\n            setUncontrolled(next)\n            onValueChange?.(next)\n          }}\n          {...props}\n        />\n      </LayoutGroup>\n    </TabsEffectContext.Provider>\n  )\n}\n\nfunction TabsList({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.List>) {\n  const context = useContext(TabsEffectContext)\n  return (\n    <TabsPrimitive.List\n      data-slot=\"tabs-list\"\n      className={cn(\n        \"inline-flex h-9 w-fit items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground\",\n        // One stacking context for the whole list keeps a flying indicator behind every label, not just its own.\n        context && [\"isolate\", EFFECT_LIST_CLASS[context.effect]],\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction TabsIndicator({ effect, reduceMotion }: Pick<TabsEffectValue, \"effect\" | \"reduceMotion\">) {\n  return (\n    <motion.span\n      aria-hidden=\"true\"\n      data-slot=\"tabs-indicator\"\n      data-reduced={reduceMotion ? \"true\" : \"false\"}\n      layoutId={reduceMotion ? undefined : \"tabs-indicator\"}\n      transition={SPRING_DEFAULT}\n      className={INDICATOR_CLASS[effect]}\n      style={effect === \"pill\" ? { borderRadius: PILL_RADIUS_PX } : undefined}\n    />\n  )\n}\n\nfunction TabsTrigger({\n  className,\n  value,\n  asChild,\n  children,\n  ...props\n}: React.ComponentProps<typeof TabsPrimitive.Trigger>) {\n  const context = useContext(TabsEffectContext)\n  // A Slot takes exactly one child, so an asChild trigger keeps the plain active style instead of an indicator.\n  const indicator = context !== null && !asChild ? context : null\n  return (\n    <TabsPrimitive.Trigger\n      data-slot=\"tabs-trigger\"\n      value={value}\n      asChild={asChild}\n      className={cn(\n        \"inline-flex h-full flex-1 items-center justify-center gap-1.5 whitespace-nowrap rounded-md border border-transparent px-2 py-1 text-sm font-medium text-muted-foreground\",\n        \"transition-[color,background-color,box-shadow] duration-(--duration-fast) ease-(--ease-mise)\",\n        \"outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50\",\n        \"disabled:pointer-events-none disabled:opacity-50\",\n        \"data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm\",\n        \"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n        indicator && [\"relative\", EFFECT_TRIGGER_CLASS[indicator.effect]],\n        className,\n      )}\n      {...props}\n    >\n      {indicator ? (\n        <>\n          {indicator.value === value ? (\n            <TabsIndicator effect={indicator.effect} reduceMotion={indicator.reduceMotion} />\n          ) : null}\n          {children}\n        </>\n      ) : (\n        children\n      )}\n    </TabsPrimitive.Trigger>\n  )\n}\n\nfunction TabsContent({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Content>) {\n  return (\n    <TabsPrimitive.Content data-slot=\"tabs-content\" className={cn(\"flex-1 outline-none\", className)} {...props} />\n  )\n}\n\nexport { Tabs, TabsList, TabsTrigger, TabsContent, type TabsEffect, type TabsProps }\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "navigation"
  ],
  "type": "registry:ui"
}