{"$schema":"https://ui.shadcn.com/schema/registry-item.json","author":"Eduardo Calvo <educlopez93@gmail.com>","css":{},"dependencies":["motion","@radix-ui/react-tabs"],"description":"A Phototab component for SmoothUI.","devDependencies":[],"files":[{"content":"\"use client\";\n\nimport {\n  Content as TabsContent,\n  List as TabsList,\n  Root as TabsRoot,\n  Trigger as TabsTrigger,\n} from \"@radix-ui/react-tabs\";\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport { useLayoutEffect, useRef, useState } from \"react\";\n\n/**\n * Tab definition for Phototab\n */\nexport interface PhototabTab {\n  /** Tab icon (ReactNode) */\n  icon: React.ReactNode;\n  /** Tab image (string: URL or import) */\n  image: string;\n  /** Tab label */\n  name: string;\n}\n\nexport interface PhototabProps {\n  /** Class name for root */\n  className?: string;\n  /** Default selected tab name */\n  defaultTab?: string;\n  /** Height of the component in pixels */\n  height?: number;\n  /** Class name for image */\n  imageClassName?: string;\n  /** Class name for tab list */\n  tabListClassName?: string;\n  /** Array of tabs to display */\n  tabs: PhototabTab[];\n  /** Class name for tab trigger */\n  tabTriggerClassName?: string;\n}\n\nexport default function Phototab({\n  tabs,\n  defaultTab,\n  height = 400,\n  className = \"\",\n  tabListClassName = \"\",\n  tabTriggerClassName = \"\",\n  imageClassName = \"\",\n}: PhototabProps) {\n  const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);\n  const [bgStyle, setBgStyle] = useState<{\n    left: number;\n    top: number;\n    width: number;\n    height: number;\n  } | null>(null);\n  const triggersRef = useRef<(HTMLButtonElement | null)[]>([]);\n  const listRef = useRef<HTMLDivElement | null>(null);\n  const shouldReduceMotion = useReducedMotion();\n\n  useLayoutEffect(() => {\n    if (\n      hoveredIndex !== null &&\n      triggersRef.current[hoveredIndex] &&\n      listRef.current\n    ) {\n      const trigger = triggersRef.current[hoveredIndex];\n      if (!trigger) {\n        return;\n      }\n      const listRect = listRef.current.getBoundingClientRect();\n      const triggerRect = trigger.getBoundingClientRect();\n      setBgStyle({\n        height: triggerRect.height,\n        left: triggerRect.left - listRect.left,\n        top: triggerRect.top - listRect.top,\n        width: triggerRect.width,\n      });\n    } else {\n      setBgStyle(null);\n    }\n  }, [hoveredIndex]);\n\n  return (\n    <TabsRoot\n      className={`group relative aspect-square w-auto overflow-hidden ${className}`}\n      defaultValue={defaultTab || (tabs[0]?.name ?? \"\")}\n      orientation=\"horizontal\"\n      style={{ height: `${height}px` }}\n    >\n      <TabsList\n        aria-label=\"Phototab Tabs\"\n        className={`absolute right-0 bottom-2 left-0 mx-auto flex w-40 -translate-y-10 flex-row items-center justify-between rounded-full bg-primary/40 px-3 py-2 font-medium text-sm ring ring-border/70 backdrop-blur-sm transition hover:text-foreground md:translate-y-20 md:group-hover:translate-y-0 ${tabListClassName}`}\n        ref={listRef}\n        style={{ pointerEvents: \"auto\" }}\n      >\n        <AnimatePresence>\n          {bgStyle ? (\n            <motion.span\n              animate={{\n                height: bgStyle.height,\n                left: bgStyle.left,\n                opacity: 1,\n                top: bgStyle.top,\n                width: bgStyle.width,\n              }}\n              className=\"absolute z-0 rounded-full bg-primary transition-colors\"\n              exit={{ opacity: 0 }}\n              initial={{ opacity: 0 }}\n              layoutId=\"hoverBackground\"\n              style={{ position: \"absolute\" }}\n              transition={\n                shouldReduceMotion\n                  ? { duration: 0 }\n                  : {\n                      damping: 40,\n                      duration: 0.25,\n                      stiffness: 400,\n                      type: \"spring\" as const,\n                    }\n              }\n            />\n          ) : null}\n        </AnimatePresence>\n        {tabs.map((tab, index) => (\n          <TabsTrigger\n            aria-label={tab.name}\n            className={`relative z-10 cursor-pointer rounded-full p-2 data-[state='active']:bg-background ${tabTriggerClassName}`}\n            key={tab.name}\n            onMouseEnter={() => {\n              setHoveredIndex(index);\n            }}\n            onMouseLeave={() => {\n              setHoveredIndex(null);\n            }}\n            ref={(el) => {\n              triggersRef.current[index] = el;\n            }}\n            value={tab.name}\n          >\n            <span className=\"relative z-10 rounded-full focus:outline-none\">\n              {tab.icon}\n              <span className=\"sr-only\">{tab.name}</span>\n            </span>\n          </TabsTrigger>\n        ))}\n      </TabsList>\n      {tabs.map((tab) => (\n        <TabsContent className=\"h-full w-full\" key={tab.name} value={tab.name}>\n          <img\n            alt={tab.name}\n            className={`h-full w-full rounded-2xl bg-primary object-cover ${imageClassName}`}\n            draggable={false}\n            height={height}\n            loading=\"lazy\"\n            src={tab.image}\n            width={400}\n          />\n        </TabsContent>\n      ))}\n    </TabsRoot>\n  );\n}\n","path":"index.tsx","target":"components/smoothui/phototab/index.tsx","type":"registry:ui"}],"name":"phototab","registryDependencies":[],"title":"Phototab","type":"registry:ui"}