{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "rolling-number",
  "title": "Rolling number",
  "description": "A number whose changed digits roll up or down, read once by screen readers.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/manniche/rolling-number/rolling-number.tsx",
      "content": "// Based on the rolling digits in Watermelon UI's “Stepper” and “Pagination”\n// (MIT, © 2026 Watermelon Platform Contributors, github.com/WatermelonCorp/watermelon-platform).\nimport { AnimatePresence, MotionConfig, motion } from 'motion/react'\nimport { useState } from 'react'\nimport { cn } from '@/lib/utils'\n\nexport type RollingNumberProps = {\n  value: number\n  /** Passed to Intl.NumberFormat, e.g. \"da-DK\". Leave it out for plain digits. */\n  locale?: string\n  className?: string\n}\n\n/** A number whose changed digits roll up or down. Screen readers get the whole number once. */\nexport function RollingNumber({ value, locale, className }: RollingNumberProps) {\n  // Remember the last value in state (not a ref), so the direction survives a double render.\n  const [last, setLast] = useState({ value, dir: 1 })\n  const dir = last.value === value ? last.dir : value > last.value ? 1 : -1\n  if (last.value !== value) setLast({ value, dir })\n  const text = locale ? new Intl.NumberFormat(locale).format(value) : String(value)\n  const chars = text.split('')\n\n  return (\n    <MotionConfig reducedMotion=\"user\">\n      <span className={cn('inline-flex tabular-nums', className)}>\n        <span className=\"sr-only\">{text}</span>\n        <span aria-hidden className=\"inline-flex\">\n          {chars.map((ch, i) => {\n            // Count from the right, so the ones stay the ones when the number grows a digit.\n            const place = chars.length - i\n            return (\n              <span key={place} className=\"relative inline-flex min-w-[1ch] justify-center overflow-hidden\">\n                <AnimatePresence mode=\"popLayout\" initial={false} custom={dir}>\n                  <motion.span\n                    key={ch}\n                    initial={{ y: `${dir * 70}%`, opacity: 0, filter: 'blur(2px)' }}\n                    animate={{ y: 0, opacity: 1, filter: 'blur(0px)' }}\n                    exit={{ y: `${dir * -70}%`, opacity: 0, filter: 'blur(2px)' }}\n                    transition={{ type: 'spring', stiffness: 260, damping: 32 }}\n                    className=\"inline-block\"\n                  >\n                    {ch}\n                  </motion.span>\n                </AnimatePresence>\n              </span>\n            )\n          })}\n        </span>\n      </span>\n    </MotionConfig>\n  )\n}\n",
      "type": "registry:component"
    }
  ],
  "meta": {
    "credit": "Adapted from Watermelon UI (MIT)."
  },
  "categories": [
    "motion"
  ],
  "type": "registry:component"
}