{
  "$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<HTMLDivElement>(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<boolean>(\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    <div\n      ref={containerRef}\n      className={cn(\n        'relative overflow-hidden w-[200px] bg-background',\n        className,\n      )}\n      style={{ height: itemHeight * displayedItemsCount, ...style }}\n      {...props}\n    >\n      <div\n        className=\"absolute z-2 top-0 inset-x-0 bg-gradient-to-t from-transparent to-background\"\n        style={{ height: itemHeight }}\n      />\n      <div\n        className=\"absolute z-1 top-0 inset-x-0 bg-background/60\"\n        style={{ height: itemHeight * sideItemsCount }}\n      />\n\n      <div\n        className=\"absolute z-1 bottom-0 inset-x-0 bg-gradient-to-b from-transparent to-background\"\n        style={{ height: itemHeight }}\n      />\n      <div\n        className=\"absolute z-1 bottom-0 inset-x-0 bg-background/60\"\n        style={{ height: itemHeight * sideItemsCount }}\n      />\n\n      <div className=\"absolute inset-x-0 top-1/2 -translate-y-1/2 flex items-center justify-center\">\n        <div\n          className=\"w-full bg-muted rounded-xl flex items-center justify-start px-6\"\n          style={{ height: itemHeight }}\n        >\n          <div className=\"relative inline-flex size-[28px] shrink-0\">\n            <Star\n              animation=\"fill\"\n              animate={isCompleted}\n              className=\"text-yellow-500\"\n            />\n            <AnimatePresence>\n              {isCompleted && (\n                <>\n                  <motion.div\n                    className=\"absolute inset-0 rounded-full\"\n                    style={{\n                      background:\n                        'radial-gradient(circle, rgba(255,215,0,0.4) 0%, rgba(255,215,0,0) 70%)',\n                    }}\n                    initial={{ scale: 1.2, opacity: 0 }}\n                    animate={{ scale: [1.2, 1.8, 1.2], opacity: [0, 0.3, 0] }}\n                    transition={{ duration: 1.2, ease: 'easeInOut' }}\n                  />\n                  <motion.div\n                    className=\"absolute inset-0 rounded-full\"\n                    style={{ boxShadow: '0 0 10px 2px rgba(255,215,0,0.6)' }}\n                    initial={{ scale: 1, opacity: 0 }}\n                    animate={{ scale: [1, 1.5], opacity: [0.8, 0] }}\n                    transition={{ duration: 0.8, ease: 'easeOut' }}\n                  />\n                  {[...Array(6)].map((_, i) => (\n                    <motion.div\n                      key={i}\n                      className=\"absolute w-1 h-1 rounded-full bg-yellow-500\"\n                      initial={{ x: '50%', y: '50%', scale: 0, opacity: 0 }}\n                      animate={{\n                        x: `calc(50% + ${Math.cos((i * Math.PI) / 3) * 30}px)`,\n                        y: `calc(50% + ${Math.sin((i * Math.PI) / 3) * 30}px)`,\n                        scale: [0, 1, 0],\n                        opacity: [0, 1, 0],\n                      }}\n                      transition={{\n                        duration: 0.8,\n                        delay: i * 0.05,\n                        ease: 'easeOut',\n                      }}\n                    />\n                  ))}\n                </>\n              )}\n            </AnimatePresence>\n          </div>\n        </div>\n      </div>\n\n      <motion.div\n        className=\"absolute left-17 bottom-0 text-start flex items-center justify-center flex-col\"\n        style={{ y: ySpring }}\n      >\n        {range.map((value) => (\n          <div\n            key={value}\n            className=\"text-2xl font-bold flex items-center justify-start w-full\"\n            style={{ height: itemHeight }}\n          >\n            {formatter.format(value)}\n          </div>\n        ))}\n      </motion.div>\n    </div>\n  );\n}\n\nexport { StarsScrollingWheel, type StarsScrollingWheelProps };\n",
      "type": "registry:ui",
      "target": "components/animate-ui/components/stars-scrolling-wheel.tsx"
    }
  ]
}