{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "features-01",
  "description": "Features section with accordion and image carousel.",
  "dependencies": [
    "motion/react",
    "@radix-ui/react-accordion",
    "lucide-react"
  ],
  "files": [
    {
      "path": "registry/blocks/features-01/page.tsx",
      "content": "import { Eye, PackageSearch, Settings } from \"lucide-react\"\n\nimport { Features } from \"@/registry/blocks/features-01/components/features\"\nimport Section from \"@/registry/blocks/features-01/components/section\"\n\nconst data = [\n  {\n    id: 1,\n    title: \"1. Choose Your Component\",\n    content:\n      \"Select the component that best suits your needs from Eldora UI's versatile collection, designed to simplify and enhance your development process.\",\n    image:\n      \"https://pub-b3533f2e1c954842824758490b95c9d5.r2.dev/block-preview-dark.png\",\n    icon: <PackageSearch className=\"text-primary size-6\" />,\n  },\n  {\n    id: 2,\n    title: \"2. Add Utility Helpers\",\n    content:\n      \"Enhance functionality by incorporating utility helpers that align with the selected component, ensuring seamless integration and customization.\",\n    image:\n      \"https://pub-b3533f2e1c954842824758490b95c9d5.r2.dev/block-preview-dark.png\",\n    icon: <Settings className=\"text-primary size-6\" />,\n  },\n  {\n    id: 3,\n    title: \"3. Copy and Paste the Code\",\n    content:\n      \"Simply copy and paste the provided code into your project, making the process quick and hassle-free. You're now ready to see the magic in action!\",\n    image:\n      \"https://pub-b3533f2e1c954842824758490b95c9d5.r2.dev/block-preview-dark.png\",\n    icon: <Eye className=\"text-primary size-6\" />,\n  },\n]\n\nexport default function Testimonals02Page() {\n  return (\n    <div className=\"flex min-h-svh w-full items-center justify-center p-6 md:p-10\">\n      <div className=\"w-full max-w-full\">\n        <Section\n          title=\"Get Started Effortlessly\"\n          subtitle=\"Three simple steps to bring your ideas to life\"\n        >\n          <Features data={data} />\n        </Section>\n      </div>\n    </div>\n  )\n}\n",
      "type": "registry:page",
      "target": "app/features/page.tsx"
    },
    {
      "path": "registry/blocks/features-01/components/features.tsx",
      "content": "\"use client\"\n\nimport type { ReactNode } from \"react\"\nimport React, { forwardRef, useEffect, useRef, useState } from \"react\"\nimport * as Accordion from \"@radix-ui/react-accordion\"\nimport { motion, useInView } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype AccordionItemProps = {\n  children: React.ReactNode\n  className?: string\n} & Accordion.AccordionItemProps\n\nconst AccordionItem = forwardRef<HTMLDivElement, AccordionItemProps>(\n  ({ children, className, ...props }, forwardedRef) => (\n    <Accordion.Item\n      className={cn(\n        \"mt-px overflow-hidden focus-within:relative focus-within:z-10\",\n        className\n      )}\n      {...props}\n      ref={forwardedRef}\n    >\n      {children}\n    </Accordion.Item>\n  )\n)\nAccordionItem.displayName = \"AccordionItem\"\n\ninterface AccordionTriggerProps {\n  children: React.ReactNode\n  className?: string\n}\n\nconst AccordionTrigger = forwardRef<HTMLButtonElement, AccordionTriggerProps>(\n  ({ children, className, ...props }, forwardedRef) => (\n    <Accordion.Header className=\"flex\">\n      <Accordion.Trigger\n        className={cn(\n          \"group flex flex-1 cursor-pointer items-center justify-between px-5 text-[15px] leading-none outline-none\",\n          className\n        )}\n        {...props}\n        ref={forwardedRef}\n      >\n        {children}\n      </Accordion.Trigger>\n    </Accordion.Header>\n  )\n)\nAccordionTrigger.displayName = \"AccordionTrigger\"\ntype AccordionContentProps = {\n  children: ReactNode\n  className?: string\n} & Accordion.AccordionContentProps\n\nconst AccordionContent = forwardRef<HTMLDivElement, AccordionContentProps>(\n  ({ children, className, ...props }, forwardedRef) => (\n    <Accordion.Content\n      className={cn(\n        \"data-[state=closed]:animate-slide-up data-[state=open]:animate-slide-down overflow-hidden text-[15px] font-medium\",\n        className\n      )}\n      {...props}\n      ref={forwardedRef}\n    >\n      <div className=\"px-5 py-2\">{children}</div>\n    </Accordion.Content>\n  )\n)\nAccordionContent.displayName = \"AccordionContent\"\n\nexport interface FeaturesDataProps {\n  id: number\n  title: string\n  content: string\n  image?: string\n  video?: string\n  icon?: React.ReactNode\n}\n\nexport interface FeaturesProps {\n  collapseDelay?: number\n  ltr?: boolean\n  linePosition?: \"left\" | \"right\" | \"top\" | \"bottom\"\n  data: FeaturesDataProps[]\n}\n\nexport function Features({\n  collapseDelay = 5000,\n  ltr = false,\n  linePosition = \"left\",\n  data = [],\n}: FeaturesProps) {\n  const [currentIndex, setCurrentIndex] = useState<number>(-1)\n  const carouselRef = useRef<HTMLUListElement>(null)\n  const ref = useRef(null)\n  const isInView = useInView(ref, {\n    once: true,\n    amount: 0.5,\n  })\n\n  useEffect(() => {\n    const timer = setTimeout(() => {\n      if (isInView) {\n        setCurrentIndex(0)\n      } else {\n        setCurrentIndex(-1)\n      }\n    }, 100)\n\n    return () => clearTimeout(timer)\n  }, [isInView])\n\n  const scrollToIndex = (index: number) => {\n    if (carouselRef.current) {\n      const card = carouselRef.current.querySelectorAll(\".card\")[index]\n      if (card) {\n        const cardRect = card.getBoundingClientRect()\n        const carouselRect = carouselRef.current.getBoundingClientRect()\n        const offset =\n          cardRect.left -\n          carouselRect.left -\n          (carouselRect.width - cardRect.width) / 2\n\n        carouselRef.current.scrollTo({\n          left: carouselRef.current.scrollLeft + offset,\n          behavior: \"smooth\",\n        })\n      }\n    }\n  }\n\n  useEffect(() => {\n    const timer = setInterval(() => {\n      setCurrentIndex((prevIndex) =>\n        prevIndex !== undefined ? (prevIndex + 1) % data.length : 0\n      )\n    }, collapseDelay)\n\n    return () => clearInterval(timer)\n  }, [collapseDelay, currentIndex, data.length])\n\n  useEffect(() => {\n    const handleAutoScroll = () => {\n      const nextIndex =\n        (currentIndex !== undefined ? currentIndex + 1 : 0) % data.length\n      scrollToIndex(nextIndex)\n    }\n\n    const autoScrollTimer = setInterval(handleAutoScroll, collapseDelay)\n\n    return () => clearInterval(autoScrollTimer)\n  }, [collapseDelay, currentIndex, data.length])\n\n  useEffect(() => {\n    const carousel = carouselRef.current\n    if (carousel) {\n      const handleScroll = () => {\n        const scrollLeft = carousel.scrollLeft\n        const cardWidth = carousel.querySelector(\".card\")?.clientWidth || 0\n        const newIndex = Math.min(\n          Math.floor(scrollLeft / cardWidth),\n          data.length - 1\n        )\n        setCurrentIndex(newIndex)\n      }\n\n      carousel.addEventListener(\"scroll\", handleScroll)\n      return () => carousel.removeEventListener(\"scroll\", handleScroll)\n    }\n  }, [data.length])\n\n  return (\n    <section ref={ref} id=\"features\">\n      <div className=\"container\">\n        <div className=\"mx-auto max-w-6xl\">\n          <div className=\"mx-auto my-12 grid h-full items-center gap-10 lg:grid-cols-2\">\n            <div\n              className={`order-1 hidden lg:order-none lg:flex ${\n                ltr ? \"lg:order-2 lg:justify-end\" : \"justify-start\"\n              }`}\n            >\n              <Accordion.Root\n                className=\"\"\n                type=\"single\"\n                defaultValue={`item-${currentIndex}`}\n                value={`item-${currentIndex}`}\n                onValueChange={(value) =>\n                  setCurrentIndex(Number(value.split(\"-\")[1]))\n                }\n              >\n                {data.map((item, index) => (\n                  <AccordionItem\n                    key={item.id}\n                    className=\"relative mb-8 last:mb-0\"\n                    value={`item-${index}`}\n                  >\n                    {linePosition === \"left\" || linePosition === \"right\" ? (\n                      <div\n                        className={`absolute inset-y-0 h-full w-0.5 overflow-hidden rounded-lg bg-neutral-300/50 dark:bg-neutral-300/30 ${\n                          linePosition === \"right\"\n                            ? \"right-0 left-auto\"\n                            : \"right-auto left-0\"\n                        }`}\n                      >\n                        <div\n                          className={`absolute top-0 left-0 w-full ${\n                            currentIndex === index ? \"h-full\" : \"h-0\"\n                          } bg-primary origin-top transition-all ease-linear dark:bg-white`}\n                          style={{\n                            transitionDuration:\n                              currentIndex === index\n                                ? `${collapseDelay}ms`\n                                : \"0s\",\n                          }}\n                        ></div>\n                      </div>\n                    ) : null}\n\n                    {linePosition === \"top\" || linePosition === \"bottom\" ? (\n                      <div\n                        className={`absolute inset-x-0 h-0.5 w-full overflow-hidden rounded-lg bg-neutral-300/50 dark:bg-neutral-300/30 ${\n                          linePosition === \"bottom\" ? \"bottom-0\" : \"top-0\"\n                        }`}\n                      >\n                        <div\n                          className={`absolute left-0 ${\n                            linePosition === \"bottom\" ? \"bottom-0\" : \"top-0\"\n                          } h-full ${\n                            currentIndex === index ? \"w-full\" : \"w-0\"\n                          } bg-primary origin-left transition-all ease-linear dark:bg-white`}\n                          style={{\n                            transitionDuration:\n                              currentIndex === index\n                                ? `${collapseDelay}ms`\n                                : \"0s\",\n                          }}\n                        ></div>\n                      </div>\n                    ) : null}\n\n                    <div className=\"relative flex items-center\">\n                      <div className=\"item-box bg-primary/10 mx-2 flex size-12 shrink-0 items-center justify-center rounded-full sm:mx-6\">\n                        {item.icon}\n                      </div>\n\n                      <div>\n                        <AccordionTrigger className=\"pl-0 text-xl font-bold\">\n                          {item.title}\n                        </AccordionTrigger>\n\n                        <AccordionTrigger className=\"justify-start pl-0 text-left text-[16px] leading-4\">\n                          {item.content}\n                        </AccordionTrigger>\n                      </div>\n                    </div>\n                  </AccordionItem>\n                ))}\n              </Accordion.Root>\n            </div>\n            <div\n              className={`h-[350px] min-h-[200px] w-auto ${\n                ltr && \"lg:order-1\"\n              }`}\n            >\n              {data[currentIndex]?.image ? (\n                <motion.img\n                  key={currentIndex}\n                  src={data[currentIndex].image}\n                  alt=\"feature\"\n                  className=\"aspect-auto size-full rounded-xl border border-neutral-300/50 object-cover object-left-top p-1 shadow-lg\"\n                  initial={{ opacity: 0, scale: 0.98 }}\n                  animate={{ opacity: 1, scale: 1 }}\n                  exit={{ opacity: 0, scale: 0.98 }}\n                  transition={{ duration: 0.25, ease: \"easeOut\" }}\n                />\n              ) : data[currentIndex]?.video ? (\n                <video\n                  preload=\"auto\"\n                  src={data[currentIndex].video}\n                  className=\"aspect-auto size-full rounded-lg object-cover shadow-lg\"\n                  autoPlay\n                  loop\n                  muted\n                />\n              ) : (\n                <div className=\"aspect-auto size-full rounded-xl border border-neutral-300/50 bg-gray-200 p-1\"></div>\n              )}\n            </div>\n\n            <ul\n              ref={carouselRef}\n              className=\"flex h-full snap-x snap-mandatory flex-nowrap overflow-x-auto [mask-image:linear-gradient(90deg,transparent,black_20%,white_80%,transparent)] py-10 [-ms-overflow-style:none] [-webkit-mask-image:linear-gradient(90deg,transparent,black_20%,white_80%,transparent)] [scrollbar-width:none] lg:hidden [&::-webkit-scrollbar]:hidden\"\n              style={{\n                padding: \"50px calc(50%)\",\n              }}\n            >\n              {data.map((item, index) => (\n                <div\n                  key={item.id}\n                  className=\"card relative mr-8 grid h-full max-w-60 shrink-0 items-start justify-center py-4 last:mr-0\"\n                  onClick={() => setCurrentIndex(index)}\n                  style={{\n                    scrollSnapAlign: \"center\",\n                  }}\n                >\n                  <div className=\"absolute inset-y-0 right-auto left-0 h-0.5 w-full overflow-hidden rounded-lg bg-neutral-300/50 dark:bg-neutral-300/30\">\n                    <div\n                      className={`absolute top-0 left-0 h-full ${\n                        currentIndex === index ? \"w-full\" : \"w-0\"\n                      } bg-primary origin-top transition-all ease-linear`}\n                      style={{\n                        transitionDuration:\n                          currentIndex === index ? `${collapseDelay}ms` : \"0s\",\n                      }}\n                    ></div>\n                  </div>\n                  <h2 className=\"text-xl font-bold\">{item.title}</h2>\n                  <p className=\"mx-0 max-w-sm text-sm text-balance\">\n                    {item.content}\n                  </p>\n                </div>\n              ))}\n            </ul>\n          </div>\n        </div>\n      </div>\n    </section>\n  )\n}\n",
      "type": "registry:component"
    },
    {
      "path": "registry/blocks/features-01/components/section.tsx",
      "content": "interface SectionProps {\n  id?: string\n  title?: string\n  subtitle?: string\n  description?: string\n  children?: React.ReactNode\n  className?: string\n}\n\nexport default function Section({\n  id,\n  title,\n  subtitle,\n  description,\n  children,\n  className,\n}: SectionProps) {\n  const sectionId = title ? title.toLowerCase().replace(/\\s+/g, \"-\") : id\n  return (\n    <section id={id || sectionId}>\n      <div className={className}>\n        <div className=\"relative container mx-auto max-w-7xl px-4 py-16\">\n          <div className=\"mx-auto space-y-4 pb-6 text-center\">\n            {title && (\n              <h2 className=\"text-primary font-mono text-sm font-medium tracking-wider uppercase\">\n                {title}\n              </h2>\n            )}\n            {subtitle && (\n              <h3 className=\"mx-auto mt-4 max-w-xs text-3xl font-semibold sm:max-w-none sm:text-4xl md:text-5xl\">\n                {subtitle}\n              </h3>\n            )}\n            {description && (\n              <p className=\"mx-auto mt-6 max-w-2xl text-lg leading-8 text-slate-600\">\n                {description}\n              </p>\n            )}\n          </div>\n          {children}\n        </div>\n      </div>\n    </section>\n  )\n}\n",
      "type": "registry:component"
    }
  ],
  "categories": [
    "features"
  ],
  "type": "registry:block"
}