{
	"$schema": "https://shadcn-svelte.com/schema/registry-item.json",
	"name": "number-ticker",
	"title": "Number Ticker",
	"type": "registry:block",
	"description": "An animated number counter component with spring physics that smoothly animates from a start value to an end value.",
	"dependencies": [
		"motion-sv"
	],
	"files": [
		{
			"content": "<script lang=\"ts\">\n\timport { onMount } from \"svelte\";\n\timport { useInView } from \"motion-sv\";\n\timport { cn } from \"$UTILS$\";\n\n\tinterface NumberTickerProps {\n\t\tvalue: number;\n\t\tstartValue?: number;\n\t\tdirection?: \"up\" | \"down\";\n\t\tdelay?: number;\n\t\tdecimalPlaces?: number;\n\t\tclass?: string;\n\t\tprefix?: string;\n\t\tsuffix?: string;\n\t\tonce?: boolean;\n\t}\n\n\tlet {\n\t\tvalue,\n\t\tstartValue = 0,\n\t\tdirection = \"up\",\n\t\tdelay = 0,\n\t\tdecimalPlaces = 0,\n\t\tclass: className,\n\t\tprefix = \"\",\n\t\tsuffix = \"\",\n\t\tonce = true,\n\t}: NumberTickerProps = $props();\n\n\tlet spanRef: HTMLSpanElement | null = $state(null);\n\n\t// Spring configuration\n\tconst damping = 60;\n\tconst stiffness = 100;\n\n\t// Spring physics simulation\n\tfunction animateValue(from: number, to: number) {\n\t\tconst startTime = performance.now();\n\t\tconst duration = 2000; // Duration in ms\n\n\t\tlet velocity = 0;\n\t\tlet position = from;\n\t\tconst target = to;\n\n\t\tfunction step(currentTime: number) {\n\t\t\tconst elapsed = currentTime - startTime;\n\t\t\tconst dt = 16.67 / 1000; // ~60fps in seconds\n\n\t\t\t// Spring physics\n\t\t\tconst springForce = -stiffness * (position - target);\n\t\t\tconst dampingForce = -damping * velocity;\n\t\t\tconst acceleration = springForce + dampingForce;\n\n\t\t\tvelocity += acceleration * dt;\n\t\t\tposition += velocity * dt;\n\n\t\t\t// Update display\n\t\t\tif (spanRef) {\n\t\t\t\tspanRef.textContent = `${prefix}${Intl.NumberFormat(\"en-US\", {\n\t\t\t\t\tminimumFractionDigits: decimalPlaces,\n\t\t\t\t\tmaximumFractionDigits: decimalPlaces,\n\t\t\t\t}).format(Number(position.toFixed(decimalPlaces)))}${suffix}`;\n\t\t\t}\n\n\t\t\t// Continue animation if not settled\n\t\t\tconst isSettled = Math.abs(velocity) < 0.01 && Math.abs(position - target) < 0.01;\n\t\t\tif (!isSettled && elapsed < duration * 2) {\n\t\t\t\trequestAnimationFrame(step);\n\t\t\t} else if (spanRef) {\n\t\t\t\t// Ensure final value is exact\n\t\t\t\tspanRef.textContent = `${prefix}${Intl.NumberFormat(\"en-US\", {\n\t\t\t\t\tminimumFractionDigits: decimalPlaces,\n\t\t\t\t\tmaximumFractionDigits: decimalPlaces,\n\t\t\t\t}).format(Number(target.toFixed(decimalPlaces)))}${suffix}`;\n\t\t\t}\n\t\t}\n\n\t\trequestAnimationFrame(step);\n\t}\n\n\tconst view = useInView(\n\t\t() => spanRef!,\n\t\t() =>\n\t\t\t({\n\t\t\t\tonce: once,\n\t\t\t\tmargin: \"0px\",\n\t\t\t}) as any\n\t);\n\t$effect(() => {\n\t\tlet timer: ReturnType<typeof setTimeout> | null = null;\n\t\tif (view.current) {\n\t\t\ttimer = setTimeout(() => {\n\t\t\t\tanimateValue(\n\t\t\t\t\tdirection === \"down\" ? value : startValue,\n\t\t\t\t\tdirection === \"down\" ? startValue : value\n\t\t\t\t);\n\t\t\t}, delay);\n\t\t}\n\t\treturn () => {\n\t\t\tif (timer !== null) {\n\t\t\t\tclearTimeout(timer);\n\t\t\t}\n\t\t};\n\t});\n</script>\n\n<span\n\tbind:this={spanRef}\n\tclass={cn(\"inline-block tracking-wider text-black tabular-nums dark:text-white\", className)}\n>\n\t{prefix}\n\t{Intl.NumberFormat(\"en-US\", {\n\t\tminimumFractionDigits: decimalPlaces,\n\t\tmaximumFractionDigits: decimalPlaces,\n\t}).format(Number((direction === \"down\" ? value : startValue).toFixed(decimalPlaces)))}\n\t{suffix}\n</span>\n",
			"type": "registry:component",
			"target": "magic/number-ticker/number-ticker.svelte"
		},
		{
			"content": "import NumberTicker from \"./number-ticker.svelte\";\nexport { NumberTicker };\n",
			"type": "registry:file",
			"target": "magic/number-ticker/index.ts"
		}
	]
}