{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"text-switcher","type":"registry:block","title":"Text switcher","description":"A reusable component that creates an engaging animation effect by smoothly transitioning between a list of phrases. The animation, driven by a moving dot, makes it ideal for dynamically completing a sentence or tagline.","dependencies":["motion"],"files":[{"path":"registry/100xui/blocks/text-switcher/components/text-switcher.tsx","content":"\"use client\";\n\nimport React from \"react\";\nimport {\n  AnimatePresence,\n  motion,\n  useMotionValue,\n  useTransform,\n  useVelocity,\n} from \"motion/react\";\n\nimport { cn } from \"@/lib/utils\";\n\nexport interface TextSwitcherProps {\n  phrases: string[];\n  readTimeInSec?: number;\n  animationDurationInSec?: number;\n  className?: string;\n  style?: React.CSSProperties;\n}\n\nconst letterVariants = {\n  initial: { scaleX: 0, opacity: 0 },\n  animate: { scaleX: 1, opacity: 1 },\n  exit: { scaleX: 0, opacity: 0 },\n};\n\nexport function TextSwitcher({\n  phrases,\n  animationDurationInSec = 0.4,\n  readTimeInSec = 2,\n  className,\n  style,\n}: TextSwitcherProps) {\n  const [currentPhrase, setCurrentPhrase] = React.useState(0);\n\n  const dotLeft = useMotionValue(\"100%\");\n  const dotLeftAsFloat = useTransform(dotLeft, (latest) => parseFloat(latest));\n  const dotVelocity = useVelocity(dotLeftAsFloat);\n\n  const dotScaleX = useTransform(dotVelocity, [-120, 0, 120], [3, 1, 3]);\n  const dotColor = useTransform(dotVelocity, (latest) => {\n    return Math.round(latest) !== 0 ? \"var(--destructive)\" : \"\";\n  });\n\n  React.useEffect(() => {\n    const totalCycleTimeInMs =\n      (readTimeInSec + 2 * animationDurationInSec) * 1000;\n    const controlInterval = setInterval(() => {\n      setCurrentPhrase((prevIndex) => (prevIndex + 1) % phrases.length);\n    }, totalCycleTimeInMs);\n\n    return () => clearInterval(controlInterval);\n  }, [phrases.length, readTimeInSec, animationDurationInSec]);\n\n  const containerVariants = {\n    animate: {\n      transition: {\n        staggerChildren: animationDurationInSec / phrases[currentPhrase].length,\n      },\n    },\n    exit: {\n      transition: {\n        staggerChildren: animationDurationInSec / phrases[currentPhrase].length,\n        staggerDirection: -1,\n      },\n    },\n  };\n\n  return (\n    <div\n      className={cn(\"inline-block\", className, \"!relative\")}\n      style={{ ...style }}\n    >\n      <AnimatePresence initial={false} mode=\"wait\">\n        <motion.div\n          key={currentPhrase}\n          className=\"flex flex-nowrap whitespace-pre\"\n          variants={containerVariants}\n          initial=\"initial\"\n          animate=\"animate\"\n          exit=\"exit\"\n        >\n          {phrases[currentPhrase].split(\"\").map((letter, i) => (\n            <motion.div\n              key={`phrases[${currentPhrase}][${i}]`}\n              className=\"origin-left\"\n              variants={letterVariants}\n              transition={{\n                duration:\n                  animationDurationInSec / phrases[currentPhrase].length,\n              }}\n            >\n              {letter}\n            </motion.div>\n          ))}\n        </motion.div>\n      </AnimatePresence>\n      <AnimatePresence initial={false} mode=\"wait\">\n        <motion.div\n          key={`phrases[${currentPhrase}].dot`}\n          style={{ left: dotLeft, scaleX: dotScaleX, color: dotColor }}\n          className=\"absolute inset-y-0\"\n          variants={{\n            initial: { left: \"0%\" },\n            animate: {\n              left: \"100%\",\n              transformOrigin: \"100% 50%\",\n              transition: {\n                duration: animationDurationInSec,\n                ease: [0.33, 1, 0.68, 1],\n              },\n            },\n            exit: {\n              left: \"0%\",\n              transformOrigin: \"0% 50%\",\n              transition: {\n                duration: animationDurationInSec,\n                ease: [0.32, 0, 0.67, 0],\n              },\n            },\n          }}\n          initial=\"initial\"\n          animate=\"animate\"\n          exit=\"exit\"\n        >\n          .\n        </motion.div>\n      </AnimatePresence>\n    </div>\n  );\n}\n","type":"registry:component"}]}