{
	"$schema": "https://shadcn-svelte.com/schema/registry-item.json",
	"name": "morphing-text",
	"title": "Morphing Text",
	"type": "registry:block",
	"description": "A text component that smoothly morphs between multiple text strings with a blur transition effect.",
	"files": [
		{
			"content": "<script lang=\"ts\">\n\timport { onMount, onDestroy } from \"svelte\";\n\timport { cn } from \"$UTILS$\";\n\n\tinterface MorphingTextProps {\n\t\ttexts: string[];\n\t\tclass?: string;\n\t}\n\n\tlet { texts, class: className }: MorphingTextProps = $props();\n\n\tconst morphTime = 1.5;\n\tconst cooldownTime = 0.5;\n\n\tlet textIndex = $state(0);\n\tlet morph = $state(0);\n\tlet cooldown = $state(0);\n\tlet time = $state(new Date());\n\n\tlet text1Ref: HTMLSpanElement | null = $state(null);\n\tlet text2Ref: HTMLSpanElement | null = $state(null);\n\n\tlet animationFrameId: number | null = null;\n\n\tconst setStyles = (fraction: number) => {\n\t\tif (!text1Ref || !text2Ref) return;\n\n\t\ttext2Ref.style.filter = `blur(${Math.min(8 / fraction - 8, 100)}px)`;\n\t\ttext2Ref.style.opacity = `${Math.pow(fraction, 0.4) * 100}%`;\n\n\t\tconst invertedFraction = 1 - fraction;\n\t\ttext1Ref.style.filter = `blur(${Math.min(8 / invertedFraction - 8, 100)}px)`;\n\t\ttext1Ref.style.opacity = `${Math.pow(invertedFraction, 0.4) * 100}%`;\n\n\t\ttext1Ref.textContent = texts[textIndex % texts.length];\n\t\ttext2Ref.textContent = texts[(textIndex + 1) % texts.length];\n\t};\n\n\tconst doMorph = () => {\n\t\tmorph -= cooldown;\n\t\tcooldown = 0;\n\n\t\tlet fraction = morph / morphTime;\n\n\t\tif (fraction > 1) {\n\t\t\tcooldown = cooldownTime;\n\t\t\tfraction = 1;\n\t\t}\n\n\t\tsetStyles(fraction);\n\n\t\tif (fraction === 1) {\n\t\t\ttextIndex++;\n\t\t}\n\t};\n\n\tconst doCooldown = () => {\n\t\tmorph = 0;\n\t\tif (text1Ref && text2Ref) {\n\t\t\ttext2Ref.style.filter = \"none\";\n\t\t\ttext2Ref.style.opacity = \"100%\";\n\t\t\ttext1Ref.style.filter = \"none\";\n\t\t\ttext1Ref.style.opacity = \"0%\";\n\t\t}\n\t};\n\n\tconst animate = () => {\n\t\tanimationFrameId = requestAnimationFrame(animate);\n\n\t\tconst newTime = new Date();\n\t\tconst dt = (newTime.getTime() - time.getTime()) / 1000;\n\t\ttime = newTime;\n\n\t\tcooldown -= dt;\n\n\t\tif (cooldown <= 0) {\n\t\t\tdoMorph();\n\t\t} else {\n\t\t\tdoCooldown();\n\t\t}\n\t};\n\n\tonMount(() => {\n\t\tanimate();\n\t});\n\n\tonDestroy(() => {\n\t\tif (animationFrameId !== null) {\n\t\t\tcancelAnimationFrame(animationFrameId);\n\t\t}\n\t});\n</script>\n\n<div\n\tclass={cn(\n\t\t\"relative mx-auto h-16 w-full max-w-3xl text-center font-sans text-[40pt] leading-none font-bold filter-[url(#threshold)_blur(0.6px)] md:h-24 lg:text-[6rem]\",\n\t\tclassName\n\t)}\n>\n\t<span bind:this={text1Ref} class=\"absolute inset-x-0 top-0 m-auto inline-block w-full\"></span>\n\t<span bind:this={text2Ref} class=\"absolute inset-x-0 top-0 m-auto inline-block w-full\"></span>\n\n\t<svg id=\"filters\" class=\"fixed h-0 w-0\" preserveAspectRatio=\"xMidYMid slice\">\n\t\t<defs>\n\t\t\t<filter id=\"threshold\">\n\t\t\t\t<feColorMatrix\n\t\t\t\t\tin=\"SourceGraphic\"\n\t\t\t\t\ttype=\"matrix\"\n\t\t\t\t\tvalues=\"1 0 0 0 0\n                  0 1 0 0 0\n                  0 0 1 0 0\n                  0 0 0 255 -140\"\n\t\t\t\t/>\n\t\t\t</filter>\n\t\t</defs>\n\t</svg>\n</div>\n",
			"type": "registry:component",
			"target": "magic/morphing-text/morphing-text.svelte"
		},
		{
			"content": "import MorphingText from \"./morphing-text.svelte\";\nexport { MorphingText };\n",
			"type": "registry:file",
			"target": "magic/morphing-text/index.ts"
		}
	]
}