{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "logo-carousal",
  "type": "registry:ui",
  "registryDependencies": [],
  "dependencies": ["motion"],
  "devDependencies": [],
  "files": [
    {
      "path": "logo-carousal.tsx",
      "content": "\"use client\";\n\nimport {\n  AnimatePresence,\n  motion,\n  useInView,\n  usePageInView,\n  useReducedMotion,\n} from \"motion/react\";\nimport type { ReactNode } from \"react\";\nimport { Children, memo, useEffect, useMemo, useRef, useState } from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst CYCLE_INTERVAL = 1600;\nconst STAGGER_DELAY = 0.125;\nconst EASE = [0.25, 0.46, 0.45, 0.94] as const;\n\nexport type LogosCarouselProps = {\n  children: ReactNode;\n  columnCount?: number;\n  direction?: \"ltr\" | \"rtl\";\n  className?: string;\n};\n\nexport function LogosCarousel({\n  children,\n  columnCount = 4,\n  direction = \"ltr\",\n  className,\n}: LogosCarouselProps) {\n  const logos = useMemo(() => Children.toArray(children), [children]);\n  const columns = useMemo(\n    () => distribute(logos, columnCount),\n    [logos, columnCount]\n  );\n\n  const reduceMotion = useReducedMotion() ?? false;\n  const containerRef = useRef<HTMLDivElement>(null);\n  const isInView = useInView(containerRef, { margin: \"100px\" });\n  const isPageInView = usePageInView();\n  const shouldPlay = !reduceMotion && isInView && isPageInView;\n\n  const [indices, setIndices] = useState(() => columns.map(() => 0));\n\n  const columnsRef = useRef(columns);\n  columnsRef.current = columns;\n\n  useEffect(() => {\n    if (!shouldPlay) return;\n    const id = setInterval(() => {\n      setIndices((prev) =>\n        columnsRef.current.map((col, i) => ((prev[i] ?? 0) + 1) % col.length)\n      );\n    }, CYCLE_INTERVAL);\n    return () => clearInterval(id);\n  }, [shouldPlay]);\n\n  return (\n    <div\n      className={cn(\"grid\", className)}\n      data-slot=\"logos-carousel\"\n      ref={containerRef}\n      style={{\n        gridTemplateColumns: `repeat(${columns.length}, minmax(0, 1fr))`,\n      }}\n    >\n      {columns.map((col, i) => (\n        <LogoColumn\n          activeIndex={(indices[i] ?? 0) % col.length}\n          delay={\n            reduceMotion\n              ? 0\n              : (direction === \"rtl\" ? columns.length - 1 - i : i) *\n                STAGGER_DELAY\n          }\n          key={i}\n          logos={col}\n          reduceMotion={reduceMotion}\n        />\n      ))}\n    </div>\n  );\n}\n\nconst LogoColumn = memo(function LogoColumn({\n  logos,\n  activeIndex,\n  delay,\n  reduceMotion,\n}: {\n  logos: ReactNode[];\n  activeIndex: number;\n  delay: number;\n  reduceMotion: boolean;\n}) {\n  return (\n    <div className=\"relative overflow-hidden\" data-slot=\"logos-carousel-column\">\n      {/* Invisible spacer — holds the column's natural height so absolutely\n          positioned logos don't collapse the grid cell. */}\n      <div\n        aria-hidden=\"true\"\n        className=\"pointer-events-none invisible select-none\"\n      >\n        {logos[0]}\n      </div>\n\n      <AnimatePresence initial={false} mode=\"sync\">\n        <motion.div\n          animate={{\n            opacity: 1,\n            y: \"0%\",\n            transition: reduceMotion\n              ? { duration: 0 }\n              : { ease: EASE, duration: 0.5, delay },\n          }}\n          className=\"absolute inset-0 flex items-center justify-center\"\n          data-slot=\"logos-carousel-logo\"\n          exit={\n            reduceMotion\n              ? undefined\n              : {\n                  opacity: 0,\n                  y: \"-60%\",\n                  transition: { ease: EASE, duration: 0.5, delay },\n                }\n          }\n          initial={reduceMotion ? false : { opacity: 0, y: \"60%\" }}\n          key={activeIndex}\n        >\n          {logos[activeIndex]}\n        </motion.div>\n      </AnimatePresence>\n    </div>\n  );\n});\n\nfunction distribute(logos: ReactNode[], count: number): ReactNode[][] {\n  const n = Math.min(count, logos.length);\n  const cols: ReactNode[][] = Array.from({ length: n }, () => []);\n  logos.forEach((logo, i) => {\n    cols[i % n].push(logo);\n  });\n  return cols;\n}\n",
      "type": "registry:ui"
    }
  ],
  "title": "Logo Carousel",
  "description": "Multi-column logo grid that cycles through sponsor logos with a staggered wave animation."
}
