{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "stars-scrolling-wheel", "type": "registry:ui", "title": "Stars Scrolling Wheel", "description": "A scrolling wheel that displays stars count.", "dependencies": [ "motion" ], "registryDependencies": [ "https://animate-ui.com/r/star-icon" ], "files": [ { "path": "registry/components/stars-scrolling-wheel/index.tsx", "content": "'use client';\n\nimport * as React from 'react';\nimport {\n AnimatePresence,\n motion,\n useMotionValue,\n useSpring,\n useTransform,\n useInView,\n type SpringOptions,\n type UseInViewOptions,\n} from 'motion/react';\n\nimport { cn } from '@/lib/utils';\nimport { Star } from '@/components/animate-ui/icons/star';\n\nconst formatter = new Intl.NumberFormat('en-US');\n\nfunction generateRange(\n max: number,\n step: number,\n sideItemsCount: number,\n): number[] {\n const result: number[] = [];\n const end = max + sideItemsCount * step;\n for (let value = end; value >= 0; value -= step) {\n result.push(value);\n }\n return result;\n}\n\ntype StarsScrollingWheelProps = {\n stars: number;\n step?: number;\n itemHeight?: number;\n sideItemsCount?: number;\n transition?: SpringOptions;\n inView?: boolean;\n inViewOnce?: boolean;\n inViewMargin?: UseInViewOptions['margin'];\n delay?: number;\n} & React.ComponentProps<'div'>;\n\nfunction StarsScrollingWheel({\n ref,\n stars,\n step = 100,\n itemHeight = 48,\n sideItemsCount = 2,\n transition = { stiffness: 90, damping: 30 },\n inView = false,\n inViewOnce = true,\n inViewMargin = '0px',\n delay = 0,\n className,\n style,\n ...props\n}: StarsScrollingWheelProps) {\n const containerRef = React.useRef(null);\n React.useImperativeHandle(ref, () => containerRef.current as HTMLDivElement);\n\n const inViewResult = useInView(containerRef, {\n once: inViewOnce,\n margin: inViewMargin,\n });\n const isInView = !inView || inViewResult;\n\n const displayedItemsCount = 1 + sideItemsCount * 2;\n const range = React.useMemo(\n () => generateRange(stars, step, sideItemsCount),\n [stars, step, sideItemsCount],\n );\n\n const initialY = -(itemHeight * sideItemsCount);\n const finalY = itemHeight * (range.length - displayedItemsCount);\n\n const yMotion = useMotionValue(initialY);\n const ySpring = useSpring(yMotion, transition);\n\n React.useEffect(() => {\n if (!isInView) return;\n const timer = setTimeout(() => {\n yMotion.set(finalY);\n }, delay);\n return () => clearTimeout(timer);\n }, [isInView, finalY, yMotion, delay]);\n\n const currentIndex = useTransform(\n ySpring,\n (y) => y / itemHeight + sideItemsCount,\n );\n const currentValue = useTransform(currentIndex, (idx) => idx * step);\n const completedTransform = useTransform(\n currentValue,\n (val) => val >= stars * 0.99,\n );\n\n const [isCompleted, setCompleted] = React.useState(\n completedTransform.get(),\n );\n React.useEffect(() => {\n const unsubscribe = completedTransform.on('change', (latest) => {\n if (latest) setCompleted(true);\n });\n return unsubscribe;\n }, [completedTransform]);\n\n return (\n \n \n \n\n \n \n\n
\n \n
\n \n \n {isCompleted && (\n <>\n \n \n {[...Array(6)].map((_, i) => (\n \n ))}\n \n )}\n \n
\n
\n \n\n \n {range.map((value) => (\n \n {formatter.format(value)}\n \n ))}\n \n \n );\n}\n\nexport { StarsScrollingWheel, type StarsScrollingWheelProps };\n", "type": "registry:ui", "target": "components/animate-ui/components/stars-scrolling-wheel.tsx" } ] }