import { useCallback, useEffect, useId, useRef, useState, type ComponentPropsWithoutRef, } from "react" import { motion } from "motion/react" import { cn } from "@/lib/utils" export interface AnimatedGridPatternProps extends ComponentPropsWithoutRef<"svg"> { width?: number height?: number x?: number y?: number strokeDasharray?: number numSquares?: number maxOpacity?: number duration?: number repeatDelay?: number } type Square = { id: number pos: [number, number] iteration: number } export function AnimatedGridPattern({ width = 40, height = 40, x = -1, y = -1, strokeDasharray = 0, numSquares = 50, className, maxOpacity = 0.5, duration = 4, repeatDelay = 0.5, ...props }: AnimatedGridPatternProps) { const id = useId() const containerRef = useRef(null) const [dimensions, setDimensions] = useState({ width: 0, height: 0 }) const [squares, setSquares] = useState>([]) const getPos = useCallback((): [number, number] => { return [ Math.floor((Math.random() * dimensions.width) / width), Math.floor((Math.random() * dimensions.height) / height), ] }, [dimensions.height, dimensions.width, height, width]) const generateSquares = useCallback( (count: number) => { return Array.from({ length: count }, (_, i) => ({ id: i, pos: getPos(), iteration: 0, })) }, [getPos] ) const updateSquarePosition = useCallback( (squareId: number) => { setSquares((currentSquares) => { const current = currentSquares[squareId] if (!current || current.id !== squareId) return currentSquares const nextSquares = currentSquares.slice() nextSquares[squareId] = { ...current, pos: getPos(), iteration: current.iteration + 1, } return nextSquares }) }, [getPos] ) useEffect(() => { if (dimensions.width && dimensions.height) { setSquares(generateSquares(numSquares)) } }, [dimensions.width, dimensions.height, generateSquares, numSquares]) useEffect(() => { const element = containerRef.current let resizeObserver: ResizeObserver | null = null if (element) { resizeObserver = new ResizeObserver((entries) => { for (const entry of entries) { setDimensions((currentDimensions) => { const nextWidth = entry.contentRect.width const nextHeight = entry.contentRect.height if ( currentDimensions.width === nextWidth && currentDimensions.height === nextHeight ) { return currentDimensions } return { width: nextWidth, height: nextHeight } }) } }) resizeObserver.observe(element) } return () => { if (resizeObserver) { resizeObserver.disconnect() } } }, []) return ( ) }