"use client" import { ElementType, useEffect, useRef, useState, useMemo } from "react" import cn from "clsx" import { motion, useAnimationControls, ValueAnimationTransition, } from "motion/react" interface GoesOutComesInUnderlineProps { /** * The content to be displayed and animated */ children: React.ReactNode /** * HTML Tag to render the component as * @default span */ as?: ElementType /** * Direction of the animation * @default "left" */ direction?: "left" | "right" /** * Optional class name for styling */ className?: string /** * Height of the underline as a ratio of font size * @default 0.1 */ underlineHeightRatio?: number /** * Padding of the underline as a ratio of font size * @default 0.01 */ underlinePaddingRatio?: number /** * Animation transition configuration * @default { duration: 0.5, ease: "easeOut" } */ transition?: ValueAnimationTransition } const GoesOutComesInUnderline = ({ children, as, direction = "left", className, underlineHeightRatio = 0.1, underlinePaddingRatio = 0.01, transition = { duration: 0.5, ease: "easeOut", }, ...props }: GoesOutComesInUnderlineProps) => { const controls = useAnimationControls() const [blocked, setBlocked] = useState(false) const textRef = useRef(null) const MotionComponent = useMemo(() => motion.create(as ?? "span"), [as]) useEffect(() => { const updateUnderlineStyles = () => { if (textRef.current) { const fontSize = parseFloat(getComputedStyle(textRef.current).fontSize) const underlineHeight = fontSize * underlineHeightRatio const underlinePadding = fontSize * underlinePaddingRatio textRef.current.style.setProperty( "--underline-height", `${underlineHeight}px` ) textRef.current.style.setProperty( "--underline-padding", `${underlinePadding}px` ) } } updateUnderlineStyles() window.addEventListener("resize", updateUnderlineStyles) return () => window.removeEventListener("resize", updateUnderlineStyles) }, [underlineHeightRatio, underlinePaddingRatio]) const animate = async () => { if (blocked) return setBlocked(true) await controls.start({ width: 0, transition, transitionEnd: { left: direction === "left" ? "auto" : 0, right: direction === "left" ? 0 : "auto", }, }) await controls.start({ width: "100%", transition, transitionEnd: { left: direction === "left" ? 0 : "", right: direction === "left" ? "" : 0, }, }) setBlocked(false) } return ( {children} ) } GoesOutComesInUnderline.displayName = "GoesOutComesInUnderline" export default GoesOutComesInUnderline