"use client";
import { cn } from "@/lib/utils";
import { motion, MotionProps } from "framer-motion";
import { useEffect, useRef, useState } from "react";
interface AnimatedSpanProps extends MotionProps {
children: React.ReactNode;
delay?: number;
className?: string;
}
export const AnimatedSpan = ({
children,
delay = 0,
className,
...props
}: AnimatedSpanProps) => (
{children}
);
interface TypingAnimationProps extends MotionProps {
children: string;
className?: string;
duration?: number;
delay?: number;
as?: React.ElementType;
}
export const TypingAnimation = ({
children,
className,
duration = 60,
delay = 0,
as: Component = "span",
...props
}: TypingAnimationProps) => {
if (typeof children !== "string") {
throw new Error("TypingAnimation: children must be a string. Received:");
}
const MotionComponent = motion.create(Component, {
forwardMotionProps: true,
});
const [displayedText, setDisplayedText] = useState("");
const [started, setStarted] = useState(false);
const elementRef = useRef(null);
useEffect(() => {
const startTimeout = setTimeout(() => {
setStarted(true);
}, delay);
return () => clearTimeout(startTimeout);
}, [delay]);
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 (
{displayedText}
);
};
interface TerminalProps {
children: React.ReactNode;
className?: string;
}
export const Terminal = ({ children, className }: TerminalProps) => {
return (
);
};