{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "marquee", "type": "registry:ui", "title": "Marquee", "description": "Infinite horizontal scrolling content via RN Animated.", "registryDependencies": [ "https://raw.githubusercontent.com/truongnat/rnui/main/registry/dist/nativewind/utils.json", "https://raw.githubusercontent.com/truongnat/rnui/main/registry/dist/nativewind/theme.json" ], "files": [ { "path": "registry/shared/ui/marquee.tsx", "type": "registry:ui", "target": "components/ui/marquee.tsx", "content": "import { useEffect, useRef, useState, type ReactNode } from 'react';\nimport { Animated, Easing, View, type ViewProps } from 'react-native';\nimport { cn } from '@/lib/utils';\n\nexport interface MarqueeProps extends ViewProps {\n /** Pixels per second. */\n speed?: number;\n className?: string;\n children?: ReactNode;\n}\n\n/** Seamless horizontal marquee — measures its content and loops two copies. */\nexport function Marquee({\n speed = 40,\n className,\n children,\n ...props\n}: MarqueeProps) {\n const x = useRef(new Animated.Value(0)).current;\n const [contentWidth, setContentWidth] = useState(0);\n\n useEffect(() => {\n if (contentWidth <= 0) return;\n x.setValue(0);\n const duration = (contentWidth / speed) * 1000;\n const loop = Animated.loop(\n Animated.timing(x, {\n toValue: -contentWidth,\n duration,\n easing: Easing.linear,\n useNativeDriver: true,\n })\n );\n loop.start();\n return () => loop.stop();\n }, [contentWidth, speed, x]);\n\n return (\n \n \n {\n const w = e.nativeEvent.layout.width;\n if (w > 0 && w !== contentWidth) setContentWidth(w);\n }}\n >\n {children}\n \n {contentWidth > 0 && {children}}\n \n \n );\n}\n" } ] }