{ "name": "scroll-text", "type": "registry:component", "dependencies": [ "motion" ], "files": [ { "type": "registry:component", "content": "\"use client\";\n\n/**\n * @author: @dorianbaffier\n * @description: Scroll Text\n * @version: 1.0.0\n * @date: 2025-06-26\n * @license: MIT\n * @website: https://kokonutui.com\n * @github: https://github.com/kokonut-labs/kokonutui\n */\n\nimport { motion, type Variants } from \"motion/react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\ninterface ScrollTextProps {\n texts?: string[];\n className?: string;\n}\n\nexport default function ScrollText({\n texts = [\n \"TailwindCSS\",\n \"Kokonut UI\",\n \"shadcn/ui\",\n \"Next.js\",\n \"Vercel\",\n \"Motion\",\n \"React\",\n \"Resend\",\n \"TypeScript\",\n \"Fumadocs\",\n \"Supabase\",\n \"Vercel\",\n ],\n className,\n}: ScrollTextProps) {\n const [activeIndex, setActiveIndex] = useState(0);\n const observerRef = useRef(null);\n const itemsRef = useRef<(HTMLDivElement | null)[]>([]);\n const containerRef = useRef(null);\n\n // Scroll to top on mount\n useEffect(() => {\n if (containerRef.current) {\n containerRef.current.scrollTop = 0;\n }\n }, []);\n\n const handleIntersection = (entries: IntersectionObserverEntry[]) => {\n entries.forEach((entry) => {\n if (entry.isIntersecting) {\n const index = itemsRef.current.findIndex(\n (item) => item === entry.target\n );\n setActiveIndex(index);\n }\n });\n };\n\n // Setup intersection observer\n const setupObserver = (element: HTMLDivElement | null, index: number) => {\n if (element && !itemsRef.current[index]) {\n itemsRef.current[index] = element;\n\n if (!observerRef.current) {\n observerRef.current = new IntersectionObserver(handleIntersection, {\n threshold: 0.7,\n root: containerRef.current,\n rootMargin: \"-45% 0px -45% 0px\",\n });\n }\n\n observerRef.current.observe(element);\n }\n };\n\n // Animation variants for the reveal effect\n const containerVariants: Variants = {\n hidden: { opacity: 0 },\n visible: {\n opacity: 1,\n transition: {\n staggerChildren: 0.1,\n },\n },\n };\n\n const itemVariants: Variants = {\n hidden: (index: number) => ({\n opacity: 0,\n x: index % 2 === 0 ? -100 : 100,\n rotate: index % 2 === 0 ? -10 : 10,\n }),\n visible: {\n opacity: 1,\n x: 0,\n rotate: 0,\n transition: {\n type: \"spring\",\n stiffness: 100,\n damping: 15,\n duration: 0.5,\n },\n },\n };\n\n return (\n
\n \n
\n \n {texts.map((text, index) => (\n setupObserver(el, index)}\n variants={itemVariants}\n viewport={{\n once: false,\n margin: \"-20% 0px -20% 0px\",\n }}\n whileInView=\"visible\"\n >\n {text}\n \n ))}\n \n
\n
\n
\n );\n}\n", "path": "/components/kokonutui/scroll-text.tsx", "target": "components/kokonutui/scroll-text.tsx" } ] }