{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "animated-testimonials", "title": "Animated testimonials", "description": "Animated testimonials block for React Native and Uniwind.", "dependencies": [ "lucide-react-native", "react-native-reanimated" ], "registryDependencies": [], "files": [ { "path": "src/blocks/animated-testimonials/animated-testimonials-block.tsx", "content": "import { ArrowLeft, ArrowRight } from 'lucide-react-native'\nimport { useCallback, useEffect, useState } from 'react'\nimport Animated, { FadeInDown, useAnimatedStyle, withSequence, withTiming } from 'react-native-reanimated'\nimport { Image, Pressable, View } from 'uniwind/components'\n\nimport { cn } from '../../../reusables/lib/utils'\nimport { UIText } from '../../reusables'\nimport type { AnimatedTestimonialsBlockProps } from '../types'\n\nfunction TestimonialImage({\n item,\n index,\n active,\n length,\n randomRotate,\n}: {\n item: any\n index: number\n active: number\n length: number\n randomRotate: number\n}) {\n const isActive = active === index\n\n const animatedStyle = useAnimatedStyle(() => {\n return {\n opacity: withTiming(isActive ? 1 : 0.7, { duration: 400 }),\n transform: [\n { scale: withTiming(isActive ? 1 : 0.95, { duration: 400 }) },\n {\n rotate: withTiming(isActive ? '0deg' : `${randomRotate}deg`, {\n duration: 400,\n }),\n },\n {\n translateY: isActive\n ? withSequence(\n withTiming(-80, { duration: 200 }),\n withTiming(0, { duration: 200 })\n )\n : withTiming(0, { duration: 400 }),\n },\n ],\n zIndex: isActive ? 40 : length + 2 - index,\n }\n })\n\n return (\n \n \n \n )\n}\n\nexport function AnimatedTestimonialsBlock({\n testimonials,\n autoplay = false,\n className,\n}: AnimatedTestimonialsBlockProps) {\n const [active, setActive] = useState(0)\n const [rotations] = useState(() =>\n testimonials.map(() => Math.floor(Math.random() * 21) - 10)\n )\n\n const handleNext = useCallback(() => {\n setActive((prev) => (prev + 1) % testimonials.length)\n }, [testimonials.length])\n\n const handlePrev = () => {\n setActive((prev) => (prev - 1 + testimonials.length) % testimonials.length)\n }\n\n useEffect(() => {\n if (autoplay) {\n const interval = setInterval(handleNext, 5000)\n return () => clearInterval(interval)\n }\n }, [autoplay, handleNext])\n\n const activeTestimonial = testimonials[active]\n\n return (\n \n \n \n \n {testimonials.map((testimonial, index) => (\n \n ))}\n \n \n\n \n \n \n {activeTestimonial.name}\n \n \n {activeTestimonial.designation}\n \n\n \n {activeTestimonial.quote.split(' ').map((word, index) => (\n \n {word}{' '}\n \n ))}\n \n \n\n \n \n \n \n \n \n \n \n \n \n \n )\n}\n", "type": "registry:component" }, { "path": "src/blocks/animated-testimonials/index.ts", "content": "export * from './animated-testimonials-block'\n", "type": "registry:lib" }, { "path": "src/blocks/types.ts", "content": "import type { ReactNode } from 'react'\n\nexport interface BlockChromeProps {\n className?: string\n description?: ReactNode\n eyebrow?: ReactNode\n title?: ReactNode\n}\n\nexport interface StatItem {\n detail?: ReactNode\n label: ReactNode\n value: ReactNode\n}\n\nexport interface FeatureItem {\n body: ReactNode\n icon?: ReactNode\n kicker?: ReactNode\n title: ReactNode\n}\n\nexport interface SurfaceItem {\n action?: ReactNode\n body: ReactNode\n icon?: ReactNode\n subtitle: ReactNode\n title: ReactNode\n}\n\nexport interface SummaryItem {\n label: ReactNode\n value: ReactNode\n}\n\nexport interface PageIntroBlockProps extends BlockChromeProps {\n actions?: ReactNode\n aside?: ReactNode\n stats?: StatItem[]\n}\n\nexport interface StatsBlockProps extends BlockChromeProps {\n items: StatItem[]\n}\n\nexport interface FeatureGridBlockProps extends BlockChromeProps {\n items: FeatureItem[]\n}\n\nexport interface CalloutBlockProps extends BlockChromeProps {\n actions?: ReactNode\n children?: ReactNode\n}\n\nexport interface SummaryPanelBlockProps extends BlockChromeProps {\n footer?: ReactNode\n items?: SummaryItem[]\n meta?: ReactNode\n}\n\nexport interface SurfaceGridBlockProps extends BlockChromeProps {\n items: SurfaceItem[]\n}\n\nexport type { Hero3Props } from './marketing/hero3'\nexport type { ShoppingCartBlockProps, ShoppingCartItem } from './shopping-cart/types'\n\nexport interface Testimonial {\n quote: string\n name: string\n designation: string\n src: string\n}\n\nexport interface AnimatedTestimonialsBlockProps {\n testimonials: Testimonial[]\n autoplay?: boolean\n className?: string\n}\n", "type": "registry:lib" } ], "docs": "Uses `lucide-react-native` (ArrowLeft, ArrowRight) and `react-native-reanimated` (FadeInDown, useAnimatedStyle). Depends on `UIText`, `cn` helper.", "categories": [ "blocks", "marketing" ], "type": "registry:block" }