{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "sliding-numbers",
  "type": "registry:block",
  "title": "Sliding numbers",
  "description": "Sliding numbers",
  "files": [
    {
      "path": "components/usages/slidingnumbersusage.tsx",
      "content": "\"use client\";\n\nimport { useEffect, useState } from \"react\";\n\nimport { SlidingNumber } from \"@/registry/open-source/sliding-numbers\";\n\nfunction Clock() {\n\tconst [hours, setHours] = useState(new Date().getHours());\n\tconst [minutes, setMinutes] = useState(new Date().getMinutes());\n\tconst [seconds, setSeconds] = useState(new Date().getSeconds());\n\n\tuseEffect(() => {\n\t\tconst interval = setInterval(() => {\n\t\t\tsetHours(new Date().getHours());\n\t\t\tsetMinutes(new Date().getMinutes());\n\t\t\tsetSeconds(new Date().getSeconds());\n\t\t}, 1000);\n\t\treturn () => clearInterval(interval);\n\t}, []);\n\n\treturn (\n\t\t<div className=\"flex items-center gap-0.5 font-mono\">\n\t\t\t<SlidingNumber value={hours} padStart={true} />\n\t\t\t<span className=\"text-secondary\">:</span>\n\t\t\t<SlidingNumber value={minutes} padStart={true} />\n\t\t\t<span className=\"text-secondary\">:</span>\n\t\t\t<SlidingNumber value={seconds} padStart={true} />\n\t\t</div>\n\t);\n}\n\nfunction SlidingNumberWithSlider() {\n\tconst [value, setValue] = useState(100);\n\n\treturn (\n\t\t<div className=\"flex flex-col items-start gap-0\">\n\t\t\t<div className=\"inline-flex items-center gap-1 font-mono leading-none\">\n\t\t\t\t$<SlidingNumber value={value} />\n\t\t\t</div>\n\t\t\t<input\n\t\t\t\ttype=\"range\"\n\t\t\t\tvalue={value}\n\t\t\t\tmin={500}\n\t\t\t\tmax={100000}\n\t\t\t\tstep={50}\n\t\t\t\tonChange={(e) => setValue(+e.target.value)}\n\t\t\t\tclassName=\"mt-2 accent-indigo-950\"\n\t\t\t/>\n\t\t</div>\n\t);\n}\n\nexport default function SlidingNumbersUsage() {\n\treturn (\n\t\t<div>\n\t\t\t<Clock />\n\t\t\t<SlidingNumberWithSlider />\n\t\t</div>\n\t);\n}\n",
      "type": "registry:block",
      "target": "~/example.tsx"
    },
    {
      "path": "components/usages/slidingnumbersusage.tsx",
      "content": "\"use client\";\n\nimport { useEffect, useState } from \"react\";\n\nimport { SlidingNumber } from \"@/registry/open-source/sliding-numbers\";\n\nfunction Clock() {\n\tconst [hours, setHours] = useState(new Date().getHours());\n\tconst [minutes, setMinutes] = useState(new Date().getMinutes());\n\tconst [seconds, setSeconds] = useState(new Date().getSeconds());\n\n\tuseEffect(() => {\n\t\tconst interval = setInterval(() => {\n\t\t\tsetHours(new Date().getHours());\n\t\t\tsetMinutes(new Date().getMinutes());\n\t\t\tsetSeconds(new Date().getSeconds());\n\t\t}, 1000);\n\t\treturn () => clearInterval(interval);\n\t}, []);\n\n\treturn (\n\t\t<div className=\"flex items-center gap-0.5 font-mono\">\n\t\t\t<SlidingNumber value={hours} padStart={true} />\n\t\t\t<span className=\"text-secondary\">:</span>\n\t\t\t<SlidingNumber value={minutes} padStart={true} />\n\t\t\t<span className=\"text-secondary\">:</span>\n\t\t\t<SlidingNumber value={seconds} padStart={true} />\n\t\t</div>\n\t);\n}\n\nfunction SlidingNumberWithSlider() {\n\tconst [value, setValue] = useState(100);\n\n\treturn (\n\t\t<div className=\"flex flex-col items-start gap-0\">\n\t\t\t<div className=\"inline-flex items-center gap-1 font-mono leading-none\">\n\t\t\t\t$<SlidingNumber value={value} />\n\t\t\t</div>\n\t\t\t<input\n\t\t\t\ttype=\"range\"\n\t\t\t\tvalue={value}\n\t\t\t\tmin={500}\n\t\t\t\tmax={100000}\n\t\t\t\tstep={50}\n\t\t\t\tonChange={(e) => setValue(+e.target.value)}\n\t\t\t\tclassName=\"mt-2 accent-indigo-950\"\n\t\t\t/>\n\t\t</div>\n\t);\n}\n\nexport default function SlidingNumbersUsage() {\n\treturn (\n\t\t<div>\n\t\t\t<Clock />\n\t\t\t<SlidingNumberWithSlider />\n\t\t</div>\n\t);\n}\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/open-source/sliding-numbers.tsx",
      "content": "\"use client\";\r\n\r\nimport { useEffect, useId } from \"react\";\r\n\r\nimport {\r\n\tmotion,\r\n\tMotionValue,\r\n\tmotionValue,\r\n\tuseSpring,\r\n\tuseTransform,\r\n} from \"motion/react\";\r\nimport useMeasure from \"react-use-measure\";\r\n\r\n// Credit:\r\n// https://motion-primitives.com/docs/sliding-number\r\n\r\nconst TRANSITION = {\r\n\ttype: \"spring\",\r\n\tstiffness: 280,\r\n\tdamping: 18,\r\n\tmass: 0.3,\r\n};\r\n\r\nfunction Digit({ value, place }: { value: number; place: number }) {\r\n\tconst valueRoundedToPlace = Math.floor(value / place) % 10;\r\n\tconst initial = motionValue(valueRoundedToPlace);\r\n\tconst animatedValue = useSpring(initial, TRANSITION);\r\n\r\n\tuseEffect(() => {\r\n\t\tanimatedValue.set(valueRoundedToPlace);\r\n\t}, [animatedValue, valueRoundedToPlace]);\r\n\r\n\treturn (\r\n\t\t<div className=\"relative inline-block w-[1ch] overflow-x-visible overflow-y-clip leading-none tabular-nums\">\r\n\t\t\t<div className=\"invisible\">0</div>\r\n\t\t\t{Array.from({ length: 10 }, (_, i) => (\r\n\t\t\t\t<Number key={i + \"sliding-numbers\"} mv={animatedValue} number={i} />\r\n\t\t\t))}\r\n\t\t</div>\r\n\t);\r\n}\r\n\r\nfunction Number({ mv, number }: { mv: MotionValue<number>; number: number }) {\r\n\tconst uniqueId = useId();\r\n\tconst [ref, bounds] = useMeasure();\r\n\r\n\tconst y = useTransform(mv, (latest) => {\r\n\t\tif (!bounds.height) return 0;\r\n\t\tconst placeValue = latest % 10;\r\n\t\tconst offset = (10 + number - placeValue) % 10;\r\n\t\tlet memo = offset * bounds.height;\r\n\r\n\t\tif (offset > 5) {\r\n\t\t\tmemo -= 10 * bounds.height;\r\n\t\t}\r\n\r\n\t\treturn memo;\r\n\t});\r\n\r\n\t// don't render the animated number until we know the height\r\n\tif (!bounds.height) {\r\n\t\treturn (\r\n\t\t\t<span ref={ref} className=\"invisible absolute\">\r\n\t\t\t\t{number}\r\n\t\t\t</span>\r\n\t\t);\r\n\t}\r\n\r\n\treturn (\r\n\t\t<motion.span\r\n\t\t\tstyle={{ y }}\r\n\t\t\tlayoutId={`${uniqueId}-${number}`}\r\n\t\t\tclassName=\"absolute inset-0 flex items-center justify-center\"\r\n\t\t\ttransition={TRANSITION}\r\n\t\t\tref={ref}\r\n\t\t>\r\n\t\t\t{number}\r\n\t\t</motion.span>\r\n\t);\r\n}\r\n\r\ntype SlidingNumberProps = {\r\n\tvalue: number;\r\n\tpadStart?: boolean;\r\n\tdecimalSeparator?: string;\r\n};\r\n\r\nexport function SlidingNumber({\r\n\tvalue,\r\n\tpadStart = false,\r\n\tdecimalSeparator = \".\",\r\n}: SlidingNumberProps) {\r\n\tconst absValue = Math.abs(value);\r\n\tconst [integerPart, decimalPart] = absValue.toString().split(\".\");\r\n\tconst integerValue = parseInt(integerPart, 10);\r\n\tconst paddedInteger =\r\n\t\tpadStart && integerValue < 10 ? `0${integerPart}` : integerPart;\r\n\tconst integerDigits = paddedInteger.split(\"\");\r\n\tconst integerPlaces = integerDigits.map((_, i) =>\r\n\t\tMath.pow(10, integerDigits.length - i - 1)\r\n\t);\r\n\r\n\treturn (\r\n\t\t<div className=\"flex items-center\">\r\n\t\t\t{value < 0 && \"-\"}\r\n\t\t\t{integerDigits.map((_, index) => (\r\n\t\t\t\t<Digit\r\n\t\t\t\t\tkey={`pos-${integerPlaces[index]}`}\r\n\t\t\t\t\tvalue={integerValue}\r\n\t\t\t\t\tplace={integerPlaces[index]}\r\n\t\t\t\t/>\r\n\t\t\t))}\r\n\t\t\t{decimalPart && (\r\n\t\t\t\t<>\r\n\t\t\t\t\t<span>{decimalSeparator}</span>\r\n\t\t\t\t\t{decimalPart.split(\"\").map((_, index) => (\r\n\t\t\t\t\t\t<Digit\r\n\t\t\t\t\t\t\tkey={`decimal-${index}`}\r\n\t\t\t\t\t\t\tvalue={parseInt(decimalPart, 10)}\r\n\t\t\t\t\t\t\tplace={Math.pow(10, decimalPart.length - index - 1)}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t))}\r\n\t\t\t\t</>\r\n\t\t\t)}\r\n\t\t</div>\r\n\t);\r\n}\r\n",
      "type": "registry:ui"
    }
  ]
}