import { useState , useEffect , useCallback , useLayoutEffect , useRef , } from "react" ; import { useDispatch , useSelector } from "react-redux" ; import * as Utils from "./utils" ; import { Action , Thunk , AppState } from "./types" ; interface Size { width : number ; height : number ; } export function useUnmountedRef ( ) { const unmountedRef = useRef ( false ) ; useEffect ( ( ) => { return ( ) => { unmountedRef . current = true ; } ; } , [ ] ) ; return unmountedRef ; } export function usePromiseValueOrNull < T > ( propValue ) { const [ value , setValue ] = useState < T | null > ( null ) ; useEffect ( ( ) => { let disposed = false ; propValue . then ( ( resolvedValue ) => { if ( disposed ) { return ; } setValue ( resolvedValue ) ; } ) ; return ( ) => { disposed = true ; } ; } , [ propValue ] ) ; return value ; } export function useScreenSize ( ) { const [ size ] = useState < Size > ( Utils . getScreenSize ( ) ) ; return size ; } export function useWindowSize ( ) { const [ size , setSize ] = useState < Size > ( Utils . getWindowSize ( ) ) ; const handler = useCallback ( Utils . throttle ( ( ) => { setSize ( Utils . getWindowSize ( ) ) ; } , 100 ) as ( ) => void , [ ] ) ; useEffect ( ( ) => { window . addEventListener ( "resize" , handler ) ; return ( ) => { window . removeEventListener ( "resize" , handler ) ; } ; } , [ handler ] ) ; return size ; } const cursorPositionRef = { current : { pageX : 0 , pageY : 0 } } ; window . document . addEventListener ( "mousemove" , ( { pageX , pageY } ) => { cursorPositionRef . current = { pageX , pageY } ; } ) ; function useCursorPositionRef ( ) { return cursorPositionRef ; } export function useIsHovered ( ) { const cursorRef = useCursorPositionRef ( ) ; const [ hover , setHover ] = useState ( false ) ; const [ node , setNode ] = useState < HTMLElement | null > ( null ) ; useLayoutEffect ( ( ) => { if ( node == null ) { setHover ( false ) ; return ; } const domRect = node . getBoundingClientRect ( ) ; const { pageX , pageY } = cursorRef . current ; setHover ( pageX >= domRect . left && pageX <= domRect . right && pageY >= domRect . top && pageY <= domRect . bottom ) ; const enter = ( ) => setHover ( true ) ; const leave = ( ) => setHover ( false ) ; node . addEventListener ( "mouseenter" , enter ) ; node . addEventListener ( "mouseleave" , leave ) ; return ( ) => { node . removeEventListener ( "mouseenter" , enter ) ; node . removeEventListener ( "mouseleave" , leave ) ; } ; } , [ node , cursorRef ] ) ; return { ref : setNode , hover } ; } export function useOnClickAway ( ref , callback ) { useEffect ( ( ) => { if ( ref == null || callback == null ) { return ; } const handleClickOut = ( ee ) => { const clickOutTarget = ee . target ; if ( ! ( clickOutTarget instanceof Element ) ) { return ; } if ( ref . contains ( clickOutTarget ) ) { return ; } callback ( ) ; window . document . removeEventListener ( "click" , handleClickOut , { capture : true , } ) ; } ; window . document . addEventListener ( "click" , handleClickOut , { capture : true , } ) ; return ( ) => { window . document . removeEventListener ( "click" , handleClickOut , { capture : true , } ) ; } ; } , [ ref , callback ] ) ; } export function useTypedSelector < T > ( selector ) { return useSelector ( selector ) ; } export function useActionCreator < T extends ( ... args : any [ ] ) => Action | Thunk > ( actionCreator ) { const dispatch = useTypedDispatch ( ) ; return useCallback ( ( ... args ) => dispatch ( actionCreator ( ... args ) ) , [ dispatch , actionCreator ] ) ; } export function useTypedDispatch ( ) { return useDispatch ( ) ; }