{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"container-text-flip","type":"registry:ui","title":"ContainerTextFlip","description":"Text container that cycles through words with per-character blur animation","author":"fancy-ui <https://fancy-ui.rama.app>","categories":["text"],"registryDependencies":["https://fancy-ui.rama.app/r/internals-dom.json","https://fancy-ui.rama.app/r/utils.json"],"files":[{"path":"react/src/components/container-text-flip/ContainerTextFlip.tsx","content":"\"use client\";\n\nimport { useEffect, useState, type HTMLAttributes } from \"react\";\nimport { cn } from \"../../utils\";\nimport { useLiveRef } from \"../../internals/dom/use-live-ref\";\nimport \"./container-text-flip.css\";\n\nexport interface ContainerTextFlipProps extends HTMLAttributes<HTMLParagraphElement> {\n\twords?: string[];\n\tinterval?: number;\n\tanimationDuration?: number;\n\tclassName?: string;\n\ttextClass?: string;\n}\n\nexport function ContainerTextFlip({\n\twords = [\"better\", \"modern\", \"beautiful\", \"awesome\"],\n\tinterval = 3000,\n\tanimationDuration = 700,\n\tclassName = \"\",\n\ttextClass = \"\",\n\t...rest\n}: ContainerTextFlipProps) {\n\tconst [currentWordIndex, setCurrentWordIndex] = useState(0);\n\tconst currentWord = words[currentWordIndex] ?? \"\";\n\tconst letters = currentWord.split(\"\");\n\n\t// The tick reads the CURRENT `words` (the source's closure reads the reactive\n\t// prop, so a swapped list changes the cycle length without restarting it) and\n\t// `interval` is captured once at arm time, exactly as the source's `onMount`\n\t// evaluates it once.\n\tconst wordsRef = useLiveRef(words);\n\tconst intervalRef = useLiveRef(interval);\n\n\t// Mount-only, as the Svelte side starts its interval in `onMount`. Keying this\n\t// on `words` would tear the timer down and re-arm it on every parent render\n\t// that passes an inline array literal — a new reference each time — which\n\t// resets the cadence forever and the word never flips.\n\tuseEffect(() => {\n\t\tconst id = setInterval(() => {\n\t\t\tsetCurrentWordIndex((index) => (index + 1) % wordsRef.current.length);\n\t\t}, intervalRef.current);\n\n\t\treturn () => clearInterval(id);\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t}, []);\n\n\treturn (\n\t\t<p\n\t\t\tclassName={cn(\n\t\t\t\t// Port-added anchor for the colocated CSS (the source relied on\n\t\t\t\t// Svelte's compiler scoping).\n\t\t\t\t\"container-text-flip\",\n\t\t\t\t\"relative inline-block rounded-lg px-4 pt-2 pb-3 text-center text-4xl font-bold text-black md:text-7xl dark:text-white\",\n\t\t\t\t\"[background:linear-gradient(to_bottom,#f3f4f6,#e5e7eb)]\",\n\t\t\t\t\"shadow-[inset_0_-1px_#d1d5db,inset_0_0_0_1px_#d1d5db,_0_4px_8px_#d1d5db]\",\n\t\t\t\t\"dark:[background:linear-gradient(to_bottom,#374151,#1f2937)]\",\n\t\t\t\t\"dark:shadow-[inset_0_-1px_#10171e,inset_0_0_0_1px_hsla(205,89%,46%,.24),_0_4px_8px_#00000052]\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t\t{...rest}\n\t\t>\n\t\t\t<span className={cn(\"inline-block\", textClass)}>\n\t\t\t\t<span className=\"inline-block\">\n\t\t\t\t\t<span key={currentWord}>\n\t\t\t\t\t\t{letters.map((letter, index) => (\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tkey={index}\n\t\t\t\t\t\t\t\tclassName=\"text-flip-letter inline-block\"\n\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\tanimationDelay: `${index * 0.02}s`,\n\t\t\t\t\t\t\t\t\tanimationDuration: `${animationDuration}ms`,\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{letter === \" \" ? \" \" : letter}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</span>\n\t\t\t\t</span>\n\t\t\t</span>\n\t\t</p>\n\t);\n}\n","type":"registry:ui","target":"@ui/fancy/components/container-text-flip/ContainerTextFlip.tsx"},{"path":"react/src/components/container-text-flip/container-text-flip.css","content":"/* Keyframe names are part of the visual contract — keep them identical to\n   the source. */\n@keyframes text-flip-in {\n\t0% {\n\t\topacity: 0;\n\t\tfilter: blur(10px);\n\t}\n\t100% {\n\t\topacity: 1;\n\t\tfilter: blur(0px);\n\t}\n}\n\n/* `.container-text-flip` is a port-added anchor: the source has no root class\n   and relied on compiler scoping, while plain CSS is global. */\n.container-text-flip .text-flip-letter {\n\tanimation: text-flip-in ease-in-out forwards;\n\topacity: 0;\n\tfilter: blur(10px);\n}\n","type":"registry:ui","target":"@ui/fancy/components/container-text-flip/container-text-flip.css"},{"path":"react/src/components/container-text-flip/index.ts","content":"\"use client\";\n\nexport { ContainerTextFlip } from \"./ContainerTextFlip\";\nexport type { ContainerTextFlipProps } from \"./ContainerTextFlip\";\n","type":"registry:ui","target":"@ui/fancy/components/container-text-flip/index.ts"}],"meta":{"license":"MIT","package":"fancy-ui-react","version":"0.7.0","source":"https://fancy-ui.rama.app/docs/components/container-text-flip?framework=react"}}