{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "testimonial-grid",
  "type": "registry:block",
  "title": "Testimonial grid",
  "description": "Testimonial grid",
  "files": [
    {
      "path": "components/usages/testimonialgridusage.tsx",
      "content": "\"use client\";\n\nimport React from \"react\";\n\nimport { TestimonialsGrid } from \"@/registry/open-source/testimonial-grid\";\n\nexport default function Usage() {\n\treturn (\n\t\t<div className=\"h-screen w-full flex items-center justify-center relative overflow-hidden bg-background\">\n\t\t\t<TestimonialsGrid />\n\t\t</div>\n\t);\n}\n",
      "type": "registry:block",
      "target": "~/example.tsx"
    },
    {
      "path": "components/usages/testimonialgridusage.tsx",
      "content": "\"use client\";\n\nimport React from \"react\";\n\nimport { TestimonialsGrid } from \"@/registry/open-source/testimonial-grid\";\n\nexport default function Usage() {\n\treturn (\n\t\t<div className=\"h-screen w-full flex items-center justify-center relative overflow-hidden bg-background\">\n\t\t\t<TestimonialsGrid />\n\t\t</div>\n\t);\n}\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/open-source/testimonial-grid.tsx",
      "content": "\"use client\";\r\n\r\nimport { useCallback, useEffect, useState } from \"react\";\r\n\r\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\";\r\nimport { ChevronLeft, ChevronRight } from \"lucide-react\";\r\nimport { AnimatePresence, motion } from \"motion/react\";\r\n\r\nconst testimonials = [\r\n\t{\r\n\t\tname: \"Shadcn\",\r\n\t\trole: \"Creator of shadcn/ui\",\r\n\t\tavatar: \"https://github.com/shadcn.png\",\r\n\t\tcontent: \"SmoothUI is my go-to for fast, beautiful UIs.\",\r\n\t},\r\n\t{\r\n\t\tname: \"Midudev\",\r\n\t\trole: \"Dev & Educator\",\r\n\t\tavatar: \"https://github.com/midudev.png\",\r\n\t\tcontent: \"Incredible DX. Animations feel native!\",\r\n\t},\r\n\t{\r\n\t\tname: \"Rauch\",\r\n\t\trole: \"Vercel CEO\",\r\n\t\tavatar: \"https://github.com/rauchg.png\",\r\n\t\tcontent: \"The best UI kit for React I've used.\",\r\n\t},\r\n\t{\r\n\t\tname: \"Pheralb\",\r\n\t\trole: \"Open Source Dev\",\r\n\t\tavatar: \"https://github.com/pheralb.png\",\r\n\t\tcontent: \"So smooth, so easy. Instantly impressive.\",\r\n\t},\r\n\t{\r\n\t\tname: \"Rauno\",\r\n\t\trole: \"UI Designer\",\r\n\t\tavatar: \"https://github.com/raunofreiberg.png\",\r\n\t\tcontent:\r\n\t\t\t\"SmoothUI's attention to detail is unmatched. Every component feels crafted with care.\",\r\n\t},\r\n\t{\r\n\t\tname: \"Emil\",\r\n\t\trole: \"Frontend Engineer\",\r\n\t\tavatar: \"https://github.com/emilkowalski.png\",\r\n\t\tcontent:\r\n\t\t\t\"Building with SmoothUI feels like having a design system that just works perfectly.\",\r\n\t},\r\n\t{\r\n\t\tname: \"Leerob\",\r\n\t\trole: \"Cursor\",\r\n\t\tavatar: \"https://github.com/emilkowalski\",\r\n\t\tcontent:\r\n\t\t\t\"SmoothUI has become essential for our team. The performance and aesthetics are outstanding.\",\r\n\t},\r\n];\r\n\r\nexport function TestimonialsGrid() {\r\n\tconst [active, setActive] = useState(0);\r\n\tconst [autoplay] = useState(false);\r\n\r\n\tconst handleNext = useCallback(() => {\r\n\t\tsetActive((prev) => (prev + 1) % testimonials.length);\r\n\t}, []);\r\n\r\n\tconst handlePrev = () => {\r\n\t\tsetActive(\r\n\t\t\t(prev) => (prev - 1 + testimonials.length) % testimonials.length\r\n\t\t);\r\n\t};\r\n\r\n\tconst isActive = (index: number) => {\r\n\t\treturn index === active;\r\n\t};\r\n\r\n\tuseEffect(() => {\r\n\t\tif (autoplay) {\r\n\t\t\tconst interval = setInterval(handleNext, 5000);\r\n\t\t\treturn () => clearInterval(interval);\r\n\t\t}\r\n\t}, [autoplay, handleNext]);\r\n\r\n\treturn (\r\n\t\t<section>\r\n\t\t\t<div className=\"bg-muted min-h-auto py-24\">\r\n\t\t\t\t<div className=\"container mx-auto w-full max-w-6xl px-6\">\r\n\t\t\t\t\t<motion.div\r\n\t\t\t\t\t\tclassName=\"mb-12\"\r\n\t\t\t\t\t\tinitial={{ opacity: 0, y: 20 }}\r\n\t\t\t\t\t\tanimate={{ opacity: 1, y: 0 }}\r\n\t\t\t\t\t\ttransition={{ duration: 0.6, ease: [0.22, 1, 0.36, 1] }}\r\n\t\t\t\t\t>\r\n\t\t\t\t\t\t{/* Layout: Title on left, Testimonial on right */}\r\n\t\t\t\t\t\t<div className=\"grid grid-cols-1 gap-8 lg:grid-cols-2 lg:gap-12\">\r\n\t\t\t\t\t\t\t{/* Left side - Title and description */}\r\n\t\t\t\t\t\t\t<div className=\"flex flex-col justify-center\">\r\n\t\t\t\t\t\t\t\t<h2 className=\"text-foreground mb-4 text-4xl font-semibold\">\r\n\t\t\t\t\t\t\t\t\tWhat Developers Say\r\n\t\t\t\t\t\t\t\t</h2>\r\n\t\t\t\t\t\t\t\t<p className=\"text-muted-foreground text-lg text-balance\">\r\n\t\t\t\t\t\t\t\t\tJoin thousands of developers who are building faster,\r\n\t\t\t\t\t\t\t\t\tmore beautiful UIs with SmoothUI. See what they're\r\n\t\t\t\t\t\t\t\t\tsaying about their experience.\r\n\t\t\t\t\t\t\t\t</p>\r\n\t\t\t\t\t\t\t</div>\r\n\r\n\t\t\t\t\t\t\t{/* Right side - Testimonial card */}\r\n\t\t\t\t\t\t\t<div className=\"relative flex max-h-fit flex-col items-end\">\r\n\t\t\t\t\t\t\t\t<div className=\"relative h-fit w-full max-w-md\">\r\n\t\t\t\t\t\t\t\t\t<AnimatePresence>\r\n\t\t\t\t\t\t\t\t\t\t{testimonials.map((testimonial, index) => (\r\n\t\t\t\t\t\t\t\t\t\t\t<motion.div\r\n\t\t\t\t\t\t\t\t\t\t\t\tkey={testimonial.name}\r\n\t\t\t\t\t\t\t\t\t\t\t\tinitial={{\r\n\t\t\t\t\t\t\t\t\t\t\t\t\topacity: 0,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tscale: 0.9,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\ty: 30,\r\n\t\t\t\t\t\t\t\t\t\t\t\t}}\r\n\t\t\t\t\t\t\t\t\t\t\t\tanimate={{\r\n\t\t\t\t\t\t\t\t\t\t\t\t\topacity: isActive(index) ? 1 : 0,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tscale: isActive(index) ? 1 : 0.95,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\ty: isActive(index) ? 0 : 30,\r\n\t\t\t\t\t\t\t\t\t\t\t\t}}\r\n\t\t\t\t\t\t\t\t\t\t\t\texit={{\r\n\t\t\t\t\t\t\t\t\t\t\t\t\topacity: 0,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tscale: 0.9,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\ty: -30,\r\n\t\t\t\t\t\t\t\t\t\t\t\t}}\r\n\t\t\t\t\t\t\t\t\t\t\t\ttransition={{\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tduration: 0.4,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tease: \"easeInOut\",\r\n\t\t\t\t\t\t\t\t\t\t\t\t}}\r\n\t\t\t\t\t\t\t\t\t\t\t\tclassName={`absolute inset-0 ${isActive(index) ? \"z-10\" : \"z-0\"}`}\r\n\t\t\t\t\t\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"bg-background rounded-2xl border px-6 py-6 shadow-lg transition-all duration-200\">\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t<motion.p\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tkey={active}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tinitial={{ opacity: 0, y: 10 }}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tanimate={{ opacity: 1, y: 0 }}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttransition={{ duration: 0.3 }}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"text-foreground mb-6 text-lg\"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{testimonial.content\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.split(\" \")\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.map((word, wordIndex) => (\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<motion.span\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tkey={`${testimonial.name}-word-${wordIndex}`}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tinitial={{\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfilter: \"blur(4px)\",\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\topacity: 0,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ty: 5,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tanimate={{\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfilter: \"blur(0px)\",\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\topacity: 1,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ty: 0,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttransition={{\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tduration: 0.2,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tease: \"easeInOut\",\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdelay: wordIndex * 0.02,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"inline-block\"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{word}&nbsp;\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</motion.span>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t))}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t</motion.p>\r\n\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t<motion.div\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"flex items-center gap-3\"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tinitial={{ opacity: 0, y: 10 }}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tanimate={{ opacity: 1, y: 0 }}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttransition={{\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tduration: 0.3,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdelay: 0.2,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<Avatar className=\"ring-foreground/10 size-8 border border-transparent ring-1 shadow-sm\">\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<AvatarImage\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsrc={testimonial.avatar}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\talt={testimonial.name}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<AvatarFallback>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{testimonial.name.charAt(0)}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</AvatarFallback>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</Avatar>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"text-foreground font-semibold\">\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{testimonial.name}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-muted-foreground text-sm\">\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{testimonial.role}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t</motion.div>\r\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\r\n\t\t\t\t\t\t\t\t\t\t\t</motion.div>\r\n\t\t\t\t\t\t\t\t\t\t))}\r\n\t\t\t\t\t\t\t\t\t</AnimatePresence>\r\n\t\t\t\t\t\t\t\t</div>\r\n\r\n\t\t\t\t\t\t\t\t{/* Navigation Arrows - Below the card */}\r\n\t\t\t\t\t\t\t\t<div className=\"absolute right-0 -bottom-16 z-20 mt-3 flex justify-center gap-2\">\r\n\t\t\t\t\t\t\t\t\t<motion.button\r\n\t\t\t\t\t\t\t\t\t\tonClick={handlePrev}\r\n\t\t\t\t\t\t\t\t\t\tclassName=\"group/button bg-background flex h-8 w-8 items-center justify-center rounded-full border shadow-lg transition-all duration-200 hover:scale-110 hover:shadow-xl\"\r\n\t\t\t\t\t\t\t\t\t\twhileHover={{ scale: 1.1 }}\r\n\t\t\t\t\t\t\t\t\t\twhileTap={{ scale: 0.95 }}\r\n\t\t\t\t\t\t\t\t\t\tinitial={{ opacity: 0, x: -20 }}\r\n\t\t\t\t\t\t\t\t\t\tanimate={{ opacity: 1, x: 0 }}\r\n\t\t\t\t\t\t\t\t\t\ttransition={{ duration: 0.3, delay: 0.4 }}\r\n\t\t\t\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t\t\t\t<ChevronLeft className=\"text-foreground h-5 w-5 transition-transform duration-300 group-hover/button:-rotate-12\" />\r\n\t\t\t\t\t\t\t\t\t</motion.button>\r\n\r\n\t\t\t\t\t\t\t\t\t<motion.button\r\n\t\t\t\t\t\t\t\t\t\tonClick={handleNext}\r\n\t\t\t\t\t\t\t\t\t\tclassName=\"group/button bg-background flex h-8 w-8 items-center justify-center rounded-full border shadow-lg transition-all duration-200 hover:scale-110 hover:shadow-xl\"\r\n\t\t\t\t\t\t\t\t\t\twhileHover={{ scale: 1.1 }}\r\n\t\t\t\t\t\t\t\t\t\twhileTap={{ scale: 0.95 }}\r\n\t\t\t\t\t\t\t\t\t\tinitial={{ opacity: 0, x: 20 }}\r\n\t\t\t\t\t\t\t\t\t\tanimate={{ opacity: 1, x: 0 }}\r\n\t\t\t\t\t\t\t\t\t\ttransition={{ duration: 0.3, delay: 0.4 }}\r\n\t\t\t\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t\t\t\t<ChevronRight className=\"text-foreground h-5 w-5 transition-transform duration-300 group-hover/button:rotate-12\" />\r\n\t\t\t\t\t\t\t\t\t</motion.button>\r\n\t\t\t\t\t\t\t\t</div>\r\n\t\t\t\t\t\t\t</div>\r\n\t\t\t\t\t\t</div>\r\n\t\t\t\t\t</motion.div>\r\n\t\t\t\t</div>\r\n\t\t\t</div>\r\n\t\t</section>\r\n\t);\r\n}\r\n",
      "type": "registry:ui"
    },
    {
      "path": "components/ui/avatar.tsx",
      "content": "\"use client\";\r\n\r\nimport React from \"react\";\r\n\r\nimport { cn } from \"@/registry/utilities/cn\";\r\nimport * as AvatarPrimitive from \"@radix-ui/react-avatar\";\r\n\r\nconst Avatar = React.forwardRef<\r\n\tReact.ElementRef<typeof AvatarPrimitive.Root>,\r\n\tReact.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>\r\n>(({ className, ...props }, ref) => (\r\n\t<AvatarPrimitive.Root\r\n\t\tref={ref}\r\n\t\tclassName={cn(\r\n\t\t\t\"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full\",\r\n\t\t\tclassName\r\n\t\t)}\r\n\t\t{...props}\r\n\t/>\r\n));\r\nAvatar.displayName = AvatarPrimitive.Root.displayName;\r\n\r\nconst AvatarImage = React.forwardRef<\r\n\tReact.ElementRef<typeof AvatarPrimitive.Image>,\r\n\tReact.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>\r\n>(({ className, ...props }, ref) => (\r\n\t<AvatarPrimitive.Image\r\n\t\tref={ref}\r\n\t\tclassName={cn(\"aspect-square h-full w-full\", className)}\r\n\t\t{...props}\r\n\t/>\r\n));\r\nAvatarImage.displayName = AvatarPrimitive.Image.displayName;\r\n\r\nconst AvatarFallback = React.forwardRef<\r\n\tReact.ElementRef<typeof AvatarPrimitive.Fallback>,\r\n\tReact.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>\r\n>(({ className, ...props }, ref) => (\r\n\t<AvatarPrimitive.Fallback\r\n\t\tref={ref}\r\n\t\tclassName={cn(\r\n\t\t\t\"flex h-full w-full items-center justify-center rounded-full bg-muted\",\r\n\t\t\tclassName\r\n\t\t)}\r\n\t\t{...props}\r\n\t/>\r\n));\r\nAvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;\r\n\r\nexport { Avatar, AvatarImage, AvatarFallback };\r\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/utilities/cn.ts",
      "content": "import { ClassValue, clsx } from \"clsx\";\r\nimport { twMerge } from \"tailwind-merge\";\r\n\r\nexport function cn(...inputs: ClassValue[]) {\r\n\treturn twMerge(clsx(inputs));\r\n}\r\n",
      "type": "registry:ui"
    }
  ]
}