{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "image-fan-slider",
  "type": "registry:ui",
  "title": "Image Fan Slider",
  "description": "An image gallery slider: a fan of photos that auto-advances — the active one large and sharp, its neighbors rotated and blurred on each side. Hover pauses. Pure CSS transitions.",
  "dependencies": [],
  "registryDependencies": [],
  "cssVars": {
    "theme": {}
  },
  "files": [
    {
      "path": "components/tweenui/image-fan-slider.tsx",
      "type": "registry:component",
      "target": "components/tweenui/image-fan-slider.tsx",
      "content": "'use client';\n\nimport { useEffect, useState, type ComponentPropsWithoutRef, type CSSProperties } from 'react';\nimport { cn } from '@/lib/utils';\n\nexport interface ImageFanSliderImage {\n  src: string;\n  alt: string;\n}\n\nexport interface ImageFanSliderProps extends ComponentPropsWithoutRef<'div'> {\n  /** Images in the fan. Five looks best. */\n  images: ImageFanSliderImage[];\n  /** Seconds between advances. */\n  interval?: number;\n  /** Autoplay direction. `left` steps forward; `right` steps backward. */\n  direction?: 'left' | 'right';\n  /** Pause autoplay while the pointer is over the fan. */\n  pauseOnHover?: boolean;\n}\n\nconst prefersReducedMotion = () =>\n  typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n\nfunction slideStyle(slideIndex: number, activeIndex: number, total: number): CSSProperties {\n  const offset = (slideIndex - activeIndex + total) % total;\n  const base: CSSProperties = {\n    zIndex: 0,\n    borderRadius: 8,\n    filter: 'blur(4px)',\n    width: 180,\n    height: 180,\n    left: '50%',\n    top: '50%',\n    transform: 'translate(-50%, -50%) scale(0.7)',\n  };\n\n  if (offset === 0) {\n    return {\n      ...base,\n      zIndex: 10,\n      filter: 'none',\n      transform: 'translate(-50%, -50%) scale(1)',\n      boxShadow: '0 0 15px rgba(0,0,0,0.25)',\n      borderRadius: 11,\n      width: 200,\n      height: 200,\n    };\n  }\n\n  if (offset === 1) {\n    return {\n      ...base,\n      zIndex: 5,\n      transform: 'translate(-50%, -50%) scale(1) translateX(185px) translateY(40px) rotate(20deg)',\n    };\n  }\n\n  if (offset === total - 1) {\n    return {\n      ...base,\n      zIndex: 5,\n      transform:\n        'translate(-50%, -50%) scale(1) translateX(-185px) translateY(40px) rotate(-20deg)',\n    };\n  }\n\n  const sign = offset <= total / 2 ? 1 : -1;\n  return {\n    ...base,\n    zIndex: 1,\n    opacity: 0,\n    transform: `translate(-50%, -50%) scale(0.87) translateX(${sign * 350}px) translateY(140px) rotate(${sign * 50}deg)`,\n  };\n}\n\nconst ImageFanSlider = ({\n  images,\n  interval = 2.5,\n  direction = 'left',\n  pauseOnHover = true,\n  className,\n  ...props\n}: ImageFanSliderProps) => {\n  const [activeIndex, setActiveIndex] = useState(0);\n  const [paused, setPaused] = useState(false);\n  const total = images.length;\n\n  useEffect(() => {\n    if (total <= 1 || prefersReducedMotion() || paused) return;\n\n    const step = direction === 'right' ? -1 : 1;\n    const id = window.setInterval(() => {\n      setActiveIndex((prev) => (prev + step + total) % total);\n    }, interval * 1000);\n\n    return () => window.clearInterval(id);\n  }, [interval, paused, total, direction]);\n\n  return (\n    <div\n      data-image-fan-slider\n      data-direction={direction}\n      className={cn(\n        'relative flex h-[280px] w-full items-start justify-center overflow-visible motion-reduce:transition-none max-sm:scale-75',\n        className\n      )}\n      {...props}\n      onPointerEnter={pauseOnHover ? () => setPaused(true) : props.onPointerEnter}\n      onPointerLeave={pauseOnHover ? () => setPaused(false) : props.onPointerLeave}\n    >\n      {images.map((image, i) => (\n        <figure\n          key={`${image.src}-${i}`}\n          data-image-fan-slide\n          data-active={i === activeIndex ? 'true' : undefined}\n          className=\"absolute overflow-hidden border-4 border-white transition-all duration-700 ease-in-out motion-reduce:transition-none dark:border-[#12161F]\"\n          style={slideStyle(i, activeIndex, total)}\n        >\n          <img src={image.src} alt={image.alt} className=\"size-full rounded-lg object-cover\" />\n        </figure>\n      ))}\n    </div>\n  );\n};\n\nexport default ImageFanSlider;\n"
    }
  ],
  "docs": "Docs, props and examples: https://tween-ui.vercel.app/component/image-fan-slider"
}