import { useState , useRef , useEffect , RefObject , createContext , PropsWithChildren , useCallback , ReactNode , } from "react" ; import { BREAKPOINTS , TBreakpoint } from "@/utils/style" ; export const viewportContext = createContext ( { } ) ; export function random ( min , max ) { return min + Math . random ( ) * ( max - min ) ; } export function useHover ( ) { const [ value , setValue ] = useState ( false ) ; const ref = useRef < HTMLDivElement > ( null ) ; function handleMouseOver ( ) { return setValue ( true ) ; } function handleMouseOut ( ) { return setValue ( false ) ; } useEffect ( ( ) => { const node = ref . current ; if ( node ) { node . addEventListener ( "mouseover" , handleMouseOver ) ; node . addEventListener ( "mouseout" , handleMouseOut ) ; return ( ) => { node . removeEventListener ( "mouseover" , handleMouseOver ) ; node . removeEventListener ( "mouseout" , handleMouseOut ) ; } ; } } , [ ] ) ; return [ ref , value ] ; } type Delay = number | null ; type CallbackFn = ( ... args : unknown [ ] ) => void ; param callback param delay export function useInterval ( callback , delay ) { const savedCallback = useRef < CallbackFn > ( ) ; useEffect ( ( ) => { savedCallback . current = callback ; } , [ callback ] ) ; useEffect ( ( ) => { const handler = ( ... args ) => savedCallback . current ?. ( ... args ) ; if ( delay !== null ) { const intervalId = setInterval ( handler , delay ) ; return ( ) => clearInterval ( intervalId ) ; } } , [ delay ] ) ; } param callback param delay export function useTimeout ( callback , delay ) { const savedCallback = useRef < CallbackFn > ( ) ; useEffect ( ( ) => { savedCallback . current = callback ; } , [ callback ] ) ; useEffect ( ( ) => { const handler = ( ... args ) => savedCallback . current ?. ( ... args ) ; if ( delay !== null ) { const id = setTimeout ( handler , delay ) ; return ( ) => clearTimeout ( id ) ; } } , [ delay ] ) ; } export function useHasMounted ( ) { const [ hasMounted , setHasMounted ] = useState ( false ) ; useEffect ( ( ) => { setHasMounted ( true ) ; } , [ ] ) ; return hasMounted ; } const QUERY = "(prefers-reduced-motion: no-preference)" ; const isRenderingOnServer = typeof window === "undefined" ; function getInitialState ( ) { return isRenderingOnServer ? true : ! window . matchMedia ( QUERY ) . matches ; } export function usePrefersReducedMotion ( ) { const [ prefersReducedMotion , setPrefersReducedMotion ] = useState ( getInitialState ) ; useEffect ( ( ) => { const mediaQueryList = window . matchMedia ( QUERY ) ; function listener ( event ) { setPrefersReducedMotion ( ! event . matches ) ; } mediaQueryList . addListener ( listener ) ; return ( ) => { mediaQueryList . removeListener ( listener ) ; } ; } , [ ] ) ; return prefersReducedMotion ; } export function useRandomInterval ( callback , minDelay , maxDelay ) { const timeoutId = useRef < number | undefined > ( ) ; const savedCallback = useRef ( callback ) ; useEffect ( ( ) => { savedCallback . current = callback ; } ) ; useEffect ( ( ) => { if ( typeof minDelay === "number" && typeof maxDelay === "number" ) { const handleTick = ( ) => { const nextTickAt = random ( minDelay , maxDelay ) ; timeoutId . current = window . setTimeout ( ( ) => { savedCallback . current ( ) ; handleTick ( ) ; } , nextTickAt ) ; } ; handleTick ( ) ; } return ( ) => window . clearTimeout ( timeoutId . current ) ; } , [ minDelay , maxDelay ] ) ; const cancel = useCallback ( function ( ) { window . clearTimeout ( timeoutId . current ) ; } , [ ] ) ; return cancel ; } export const ViewportProvider = ( { children } ) => { const [ width , setWidth ] = useState ( window ?. innerWidth ) ; function handleWindowResize ( ) { setWidth ( window . innerWidth ) ; } useEffect ( ( ) => { window . addEventListener ( "resize" , handleWindowResize ) ; return ( ) => window . removeEventListener ( "resize" , handleWindowResize ) ; } , [ ] ) ; return < viewportContext . Provider value = { { width } } > { children } < /viewportContext.Provider>; } ; function getBreakpointOrSize ( breakpoint ) { if ( typeof breakpoint === "string" ) { return BREAKPOINTS [ breakpoint ] ; } return breakpoint ; } type TUseBreakpointProps = ( { from , to } : { from ? : TBreakpoint | number ; to : TBreakpoint | number ; } , { onEnter , onLeave } : { onEnter : ( ) => void ; onLeave : ( ) => void ; } ) => void ; param viewportSize param callbackFn export const useBreakpointRange = ( { from = 0 , to } , { onEnter , onLeave } ) => { const [ currentState , setCurrentState ] = useState ( "" ) ; const fromSize = getBreakpointOrSize ( from ) ; const toSize = getBreakpointOrSize ( to ) ; useEffect ( ( ) => { function handleResize ( ) { const screenWidth = window . innerWidth ; const withinFrom = screenWidth > fromSize ; const withinTo = ! toSize || screenWidth < toSize ; const newState = `${ withinFrom }-${ withinTo }` ; if ( currentState !== newState ) { setCurrentState ( newState ) ; if ( withinFrom && withinTo && onEnter ) { onEnter ( ) ; } else if ( onLeave ) { onLeave ( ) ; } } } handleResize ( ) ; window . addEventListener ( "resize" , handleResize ) ; return ( ) => window . removeEventListener ( "resize" , handleResize ) ; } , [ fromSize , toSize , onEnter , onLeave , currentState , setCurrentState ] ) ; } ;