import { motion } from "motion/react" import { useEffect, useRef, useState } from "react" import { cn } from "@/lib/utils" /** Pixels a second. Slow enough to read, quick enough not to be a wait. */ const SPEED = 25 /** Long enough to read the start before it moves, and again at the far end. */ const DWELL = 1.2 /** * Text that scrolls its own overflow into view and back. * * A long message name, or a value that does not fit, would otherwise widen * whatever holds it until the panel around it gives way. This keeps the width * the layout asked for and moves the text instead — and stays perfectly still * when it already fits, so a column of these is not a column of motion. * * Reduced motion is handled by the `MotionConfig reducedMotion="user"` each app * root is wrapped in: the text simply sits at its start. */ export function Marquee({ text, className, }: { text: string className?: string }) { const ref = useRef(null) const [overflow, setOverflow] = useState(0) // biome-ignore lint/correctness/useExhaustiveDependencies: a new string is what changes the measurement. useEffect(() => { const el = ref.current if (!el) return const measure = () => setOverflow(Math.max(0, el.scrollWidth - el.clientWidth)) measure() // The panel it sits in can be resized, and the same text overflows or does // not depending on how much room it was given. const observer = new ResizeObserver(measure) observer.observe(el) return () => observer.disconnect() }, [text]) return ( {text} ) }