{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "animated-testimonials",
  "title": "Animated Testimonials",
  "description": "Testimonial carousel with animated transitions between quotes.",
  "dependencies": [
    "@vllnt/ui@^0.2.1"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/animated-testimonials/animated-testimonials.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"@vllnt/ui\";\n\n/** Single testimonial entry. */\nexport type Testimonial = {\n  /** Author name. */\n  name: string;\n  /** Body of the testimonial. */\n  quote: string;\n  /** Author role or company. */\n  title: string;\n};\n\n/** Props for {@link AnimatedTestimonials}. */\nexport type AnimatedTestimonialsProps =\n  React.ComponentPropsWithoutRef<\"div\"> & {\n    /** Advance through entries on a timer. Defaults to `false`. */\n    autoplay?: boolean;\n    /** Testimonials to cycle through. */\n    testimonials: Testimonial[];\n  };\n\n/**\n * Carousel of testimonials with previous and next controls.\n *\n * Respects `prefers-reduced-motion`: entries swap without sliding.\n *\n * @example\n * ```tsx\n * <AnimatedTestimonials testimonials={items} autoplay />\n * ```\n */\nexport const AnimatedTestimonials = ({\n  autoplay = false,\n  className,\n  ref,\n  testimonials,\n  ...props\n}: AnimatedTestimonialsProps & { ref?: React.Ref<HTMLDivElement> }) => {\n  const [activeIndex, setActiveIndex] = React.useState(0);\n  const count = testimonials.length;\n\n  const goTo = React.useCallback(\n    (step: number): void => {\n      setActiveIndex((current) => (current + step + count) % count);\n    },\n    [count],\n  );\n\n  React.useEffect(() => {\n    if (!autoplay || count <= 1) {\n      return;\n    }\n\n    const timer = setInterval(() => {\n      goTo(1);\n    }, 5000);\n\n    return () => {\n      clearInterval(timer);\n    };\n  }, [autoplay, count, goTo]);\n\n  const active = testimonials[activeIndex];\n\n  if (active === undefined) {\n    return null;\n  }\n\n  return (\n    <div\n      className={cn(\n        \"flex flex-col gap-4 rounded-xl border bg-card p-6 text-card-foreground shadow-sm\",\n        className,\n      )}\n      ref={ref}\n      {...props}\n    >\n      <TestimonialCard index={activeIndex} testimonial={active} />\n      <div className=\"flex items-center justify-between\">\n        <button\n          className=\"rounded-md border px-3 py-1 text-sm transition-colors hover:bg-accent hover:text-accent-foreground\"\n          onClick={() => {\n            goTo(-1);\n          }}\n          type=\"button\"\n        >\n          Previous\n        </button>\n        <button\n          className=\"rounded-md border px-3 py-1 text-sm transition-colors hover:bg-accent hover:text-accent-foreground\"\n          onClick={() => {\n            goTo(1);\n          }}\n          type=\"button\"\n        >\n          Next\n        </button>\n      </div>\n    </div>\n  );\n};\nAnimatedTestimonials.displayName = \"AnimatedTestimonials\";\n\nfunction TestimonialCard({\n  index,\n  testimonial,\n}: {\n  index: number;\n  testimonial: Testimonial;\n}) {\n  return (\n    <figure\n      className=\"animate-in fade-in-0 slide-in-from-right-4 motion-reduce:animate-none\"\n      key={index}\n    >\n      <blockquote className=\"text-lg text-foreground\">\n        {testimonial.quote}\n      </blockquote>\n      <figcaption className=\"mt-2 text-sm text-muted-foreground\">\n        {testimonial.name} — {testimonial.title}\n      </figcaption>\n    </figure>\n  );\n}\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component",
  "version": "0.2.1",
  "stability": "stable"
}
