{ "name": "carousel", "type": "registry:ui", "registryDependencies": [], "dependencies": [ "motion" ], "devDependencies": [], "tailwind": {}, "cssVars": { "light": {}, "dark": {} }, "files": [ { "path": "carousel.tsx", "content": "'use client';\nimport {\n Children,\n ReactNode,\n createContext,\n useContext,\n useEffect,\n useRef,\n useState,\n} from 'react';\nimport { motion, Transition, useMotionValue } from 'motion/react';\nimport { cn } from '@/lib/utils';\nimport { ChevronLeft, ChevronRight } from 'lucide-react';\n\nexport type CarouselContextType = {\n index: number;\n setIndex: (newIndex: number) => void;\n itemsCount: number;\n setItemsCount: (newItemsCount: number) => void;\n disableDrag: boolean;\n};\n\nconst CarouselContext = createContext(\n undefined\n);\n\nfunction useCarousel() {\n const context = useContext(CarouselContext);\n if (!context) {\n throw new Error('useCarousel must be used within an CarouselProvider');\n }\n return context;\n}\n\nexport type CarouselProviderProps = {\n children: ReactNode;\n initialIndex?: number;\n onIndexChange?: (newIndex: number) => void;\n disableDrag?: boolean;\n};\n\nfunction CarouselProvider({\n children,\n initialIndex = 0,\n onIndexChange,\n disableDrag = false,\n}: CarouselProviderProps) {\n const [index, setIndex] = useState(initialIndex);\n const [itemsCount, setItemsCount] = useState(0);\n\n const handleSetIndex = (newIndex: number) => {\n setIndex(newIndex);\n onIndexChange?.(newIndex);\n };\n\n useEffect(() => {\n setIndex(initialIndex);\n }, [initialIndex]);\n\n return (\n \n {children}\n \n );\n}\n\nexport type CarouselProps = {\n children: ReactNode;\n className?: string;\n initialIndex?: number;\n index?: number;\n onIndexChange?: (newIndex: number) => void;\n disableDrag?: boolean;\n};\n\nfunction Carousel({\n children,\n className,\n initialIndex = 0,\n index: externalIndex,\n onIndexChange,\n disableDrag = false,\n}: CarouselProps) {\n const [internalIndex, setInternalIndex] = useState(initialIndex);\n const isControlled = externalIndex !== undefined;\n const currentIndex = isControlled ? externalIndex : internalIndex;\n\n const handleIndexChange = (newIndex: number) => {\n if (!isControlled) {\n setInternalIndex(newIndex);\n }\n onIndexChange?.(newIndex);\n };\n\n return (\n \n
\n
{children}
\n
\n \n );\n}\n\nexport type CarouselNavigationProps = {\n className?: string;\n classNameButton?: string;\n alwaysShow?: boolean;\n};\n\nfunction CarouselNavigation({\n className,\n classNameButton,\n alwaysShow,\n}: CarouselNavigationProps) {\n const { index, setIndex, itemsCount } = useCarousel();\n\n return (\n \n {\n if (index > 0) {\n setIndex(index - 1);\n }\n }}\n >\n \n \n {\n if (index < itemsCount - 1) {\n setIndex(index + 1);\n }\n }}\n >\n \n \n \n );\n}\n\nexport type CarouselIndicatorProps = {\n className?: string;\n classNameButton?: string;\n};\n\nfunction CarouselIndicator({\n className,\n classNameButton,\n}: CarouselIndicatorProps) {\n const { index, itemsCount, setIndex } = useCarousel();\n\n return (\n \n
\n {Array.from({ length: itemsCount }, (_, i) => (\n setIndex(i)}\n className={cn(\n 'h-2 w-2 rounded-full transition-opacity duration-300',\n index === i\n ? 'bg-zinc-950 dark:bg-zinc-50'\n : 'bg-zinc-900/50 dark:bg-zinc-100/50',\n classNameButton\n )}\n />\n ))}\n
\n \n );\n}\n\nexport type CarouselContentProps = {\n children: ReactNode;\n className?: string;\n transition?: Transition;\n};\n\nfunction CarouselContent({\n children,\n className,\n transition,\n}: CarouselContentProps) {\n const { index, setIndex, setItemsCount, disableDrag } = useCarousel();\n const [visibleItemsCount, setVisibleItemsCount] = useState(1);\n const dragX = useMotionValue(0);\n const containerRef = useRef(null);\n const itemsLength = Children.count(children);\n\n useEffect(() => {\n if (!containerRef.current) {\n return;\n }\n\n const options = {\n root: containerRef.current,\n threshold: 0.5,\n };\n\n const observer = new IntersectionObserver((entries) => {\n const visibleCount = entries.filter(\n (entry) => entry.isIntersecting\n ).length;\n setVisibleItemsCount(visibleCount);\n }, options);\n\n const childNodes = containerRef.current.children;\n Array.from(childNodes).forEach((child) => observer.observe(child));\n\n return () => observer.disconnect();\n }, [children, setItemsCount]);\n\n useEffect(() => {\n if (!itemsLength) {\n return;\n }\n\n setItemsCount(itemsLength);\n }, [itemsLength, setItemsCount]);\n\n const onDragEnd = () => {\n const x = dragX.get();\n\n if (x <= -10 && index < itemsLength - 1) {\n setIndex(index + 1);\n } else if (x >= 10 && index > 0) {\n setIndex(index - 1);\n }\n };\n\n return (\n \n {children}\n \n );\n}\n\nexport type CarouselItemProps = {\n children: ReactNode;\n className?: string;\n};\n\nfunction CarouselItem({ children, className }: CarouselItemProps) {\n return (\n \n {children}\n \n );\n}\n\nexport {\n Carousel,\n CarouselContent,\n CarouselNavigation,\n CarouselIndicator,\n CarouselItem,\n useCarousel,\n};\n", "type": "registry:ui" } ] }