{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "number-counter",
  "type": "registry:ui",
  "title": "Number Counter",
  "description": "An animated stats counter whose digits spin up once when the stat scrolls into view. ScrollTrigger fires it, Number Flow handles the count.",
  "dependencies": [
    "gsap",
    "@gsap/react",
    "@number-flow/react"
  ],
  "registryDependencies": [],
  "cssVars": {
    "theme": {}
  },
  "files": [
    {
      "path": "components/tweenui/number-counter.tsx",
      "type": "registry:component",
      "target": "components/tweenui/number-counter.tsx",
      "content": "'use client';\n\nimport { useRef, useState, type ComponentPropsWithoutRef } from 'react';\nimport { useGSAP } from '@gsap/react';\nimport NumberFlow, { type Format } from '@number-flow/react';\nimport gsap from 'gsap';\nimport { ScrollTrigger } from 'gsap/ScrollTrigger';\nimport { cn } from '@/lib/utils';\n\ngsap.registerPlugin(useGSAP, ScrollTrigger);\n\nconst prefersReducedMotion = () =>\n  typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n\nconst DEFAULT_DURATION = 1.8;\n\nexport interface NumberCounterProps extends ComponentPropsWithoutRef<'span'> {\n  value: number;\n  delay?: number;\n  duration?: number;\n  instant?: boolean;\n  format?: Format;\n}\n\nconst NumberCounter = ({\n  value,\n  delay = 0,\n  duration = DEFAULT_DURATION,\n  instant = false,\n  format,\n  className,\n  ...props\n}: NumberCounterProps) => {\n  const rootRef = useRef<HTMLSpanElement>(null);\n  const [displayValue, setDisplayValue] = useState(0);\n\n  useGSAP(\n    () => {\n      const root = rootRef.current;\n      if (!root) return;\n\n      let startTween: gsap.core.Tween | null = null;\n      const play = () => {\n        startTween = gsap.delayedCall(delay, () => setDisplayValue(value));\n      };\n\n      if (prefersReducedMotion()) {\n        setDisplayValue(value);\n        return;\n      }\n\n      if (instant) {\n        play();\n        return () => startTween?.kill();\n      }\n\n      const trigger = ScrollTrigger.create({\n        trigger: root,\n        scroller: root.closest('[data-counter-scroller]') ?? undefined,\n        start: 'top 85%',\n        once: true,\n        onEnter: play,\n      });\n\n      return () => {\n        startTween?.kill();\n        trigger.kill();\n      };\n    },\n    { scope: rootRef, dependencies: [value, delay, instant] }\n  );\n\n  const durationMs = prefersReducedMotion() ? 0 : duration * 1000;\n\n  return (\n    <span\n      ref={rootRef}\n      data-counter-trigger\n      className={cn('motion-reduce:transition-none', className)}\n      {...props}\n    >\n      <NumberFlow\n        value={displayValue}\n        format={{ useGrouping: true, ...format }}\n        trend={0}\n        transformTiming={{ duration: durationMs, easing: 'ease-out' }}\n        spinTiming={{ duration: durationMs, easing: 'ease-out' }}\n        opacityTiming={{\n          duration: prefersReducedMotion() ? 0 : Math.max(250, duration * 450),\n          easing: 'ease-out',\n        }}\n      />\n    </span>\n  );\n};\n\nexport default NumberCounter;\n"
    }
  ],
  "docs": "Docs, props and examples: https://tween-ui.vercel.app/component/number-counter\nGSAP plugins (ScrollTrigger) are registered inside the component, so there is nothing to set up."
}