{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"number-ticker","type":"registry:component","title":"Number Ticker","description":"Number that rolls to a new value like an odometer, one changed digit at a time.","author":"Ryan","dependencies":["clsx","motion","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/number-ticker.tsx","type":"registry:component","target":"@components/motion/number-ticker.tsx","content":"\"use client\";\n// easeui.dev/components/motion/number-ticker\n\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport { cn } from \"@/lib/utils\";\n\nconst EASE = [0.23, 1, 0.32, 1] as const;\n\nexport interface NumberTickerProps {\n  /** The number to display. Changing it rolls each digit to its new value. */\n  value: number;\n  /** Formats the number before it is split into characters. Default: grouped with commas. */\n  format?: (value: number) => string;\n  className?: string;\n}\n\nconst defaultFormat = (value: number) => Math.round(value).toLocaleString(\"en-US\");\n\n/**\n * A number that rolls to a new value like an odometer: each character that\n * changes slides up and out while its replacement slides up into place.\n * Characters keep their slot counting from the right, so a comma or a new\n * leading digit never disturbs the ones already on screen.\n */\nexport function NumberTicker({ value, format = defaultFormat, className }: NumberTickerProps) {\n  const reduce = useReducedMotion();\n  const text = format(value);\n  const characters = text.split(\"\");\n\n  return (\n    <span className={cn(\"inline-flex tabular-nums\", className)}>\n      <span aria-hidden=\"true\" className=\"inline-flex\">\n        {characters.map((char, index) => (\n          <span\n            // biome-ignore lint/suspicious/noArrayIndexKey: the key is distance from the right edge, stable as digits are added.\n            key={characters.length - index}\n            className=\"relative inline-block overflow-hidden\"\n          >\n            <span className=\"invisible\">{char}</span>\n            <AnimatePresence mode=\"popLayout\" initial={false}>\n              <motion.span\n                key={char}\n                initial={reduce ? false : { y: \"70%\", opacity: 0 }}\n                animate={{ y: \"0%\", opacity: 1 }}\n                exit={reduce ? undefined : { y: \"-70%\", opacity: 0 }}\n                transition={{ duration: reduce ? 0 : 0.35, ease: EASE }}\n                className=\"absolute inset-0\"\n              >\n                {char}\n              </motion.span>\n            </AnimatePresence>\n          </span>\n        ))}\n      </span>\n      <span className=\"sr-only\">{text}</span>\n    </span>\n  );\n}\n"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts","content":"import { clsx, type ClassValue } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs))\n}\n"}]}