{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "animated-counter", "title": "Animated Counter", "description": "Number counter that animates into view with easing, supporting prefix, suffix, and decimals.", "dependencies": [ "motion" ], "files": [ { "path": "registry/pioneerui/animated-counter.tsx", "content": "\"use client\"\n\nimport React, { useEffect, useRef, useState } from \"react\"\nimport { motion, useInView, useMotionValue, useTransform, animate } from \"motion/react\"\nimport { cn } from \"@/lib/utils\"\n\nexport interface AnimatedCounterProps {\n from?: number\n to: number\n duration?: number\n /** Optional suffix like \"+\", \"k\", \"%\" */\n suffix?: string\n /** Optional prefix like \"$\" */\n prefix?: string\n className?: string\n /** Decimal places (default 0) */\n decimals?: number\n}\n\nexport function AnimatedCounter({\n from = 0,\n to,\n duration = 2,\n suffix = \"\",\n prefix = \"\",\n className,\n decimals = 0,\n}: AnimatedCounterProps) {\n const ref = useRef(null)\n const inView = useInView(ref, { once: true, margin: \"0px 0px -40px 0px\" })\n const count = useMotionValue(from)\n const rounded = useTransform(count, (v) => {\n const fixed = v.toFixed(decimals)\n // Add thousands separator\n const [int, dec] = fixed.split(\".\")\n const formatted = int.replace(/\\B(?=(\\d{3})+(?!\\d))/g, \",\")\n return dec !== undefined ? `${formatted}.${dec}` : formatted\n })\n\n useEffect(() => {\n if (!inView) return\n const controls = animate(count, to, {\n duration,\n ease: [0.25, 0.46, 0.45, 0.94],\n })\n return controls.stop\n }, [inView, count, to, duration])\n\n return (\n \n {prefix}\n {rounded}\n {suffix}\n \n )\n}\n\nexport interface StatCardProps extends React.HTMLAttributes {\n stats: Array<{\n label: string\n value: number\n prefix?: string\n suffix?: string\n decimals?: number\n }>\n}\n\nexport function StatCard({ stats, className, ...props }: StatCardProps) {\n return (\n \n {stats.map((stat, i) => (\n
\n \n \n \n {stat.label}\n
\n ))}\n \n )\n}\n", "type": "registry:ui", "target": "components/pioneerui/animated-counter.tsx" } ], "type": "registry:ui" }