UNPKG

xlibs-components

Version:

Modern React component library with Aceternity, MagicUI, and ShadCN components for building beautiful web applications

52 lines (51 loc) 1.92 kB
"use client"; import { jsx as _jsx } from "react/jsx-runtime"; import { cn } from '../../lib/utils.js'; import { motion } from "motion/react"; import { useEffect, useRef, useState } from "react"; export function TypingAnimation({ children, className, duration = 100, delay = 0, as: Component = "div", startOnView = false, ...props }) { const MotionComponent = motion.create(Component, { forwardMotionProps: true, }); const [displayedText, setDisplayedText] = useState(""); const [started, setStarted] = useState(false); const elementRef = useRef(null); useEffect(() => { if (!startOnView) { const startTimeout = setTimeout(() => { setStarted(true); }, delay); return () => clearTimeout(startTimeout); } const observer = new IntersectionObserver(([entry]) => { if (entry.isIntersecting) { setTimeout(() => { setStarted(true); }, delay); observer.disconnect(); } }, { threshold: 0.1 }); if (elementRef.current) { observer.observe(elementRef.current); } return () => observer.disconnect(); }, [delay, startOnView]); useEffect(() => { if (!started) return; let i = 0; const typingEffect = setInterval(() => { if (i < children.length) { setDisplayedText(children.substring(0, i + 1)); i++; } else { clearInterval(typingEffect); } }, duration); return () => { clearInterval(typingEffect); }; }, [children, duration, started]); return (_jsx(MotionComponent, { ref: elementRef, className: cn("text-4xl font-bold leading-[5rem] tracking-[-0.02em]", className), ...props, children: displayedText })); }