/** * External dependencies */ import { useRef, useLayoutEffect, useState } from '@wordpress/element'; /** @typedef {import('react')} React */ /** @type {React.CSSProperties} */ const style = { bottom: 0, left: 0, opacity: 0, pointerEvents: 'none', position: 'absolute', right: 0, top: 0, zIndex: -1, }; /** * Returns an element and a string (`above`, `visible` or `below`) based on the * element position relative to the viewport. * _Note: `usePositionRelativeToViewport` will return an empty position (``) * until after first render_ * * @return {Array} An array of {Element} `referenceElement` and {string} `positionRelativeToViewport`. * * @example * * ```js * const App = () => { * const [ referenceElement, positionRelativeToViewport ] = useContainerQueries(); * * return ( * <> * { referenceElement } * { positionRelativeToViewport === 'below' &&

Reference element is below the viewport.

} * { positionRelativeToViewport === 'visible' &&

Reference element is visible in the viewport.

} * { positionRelativeToViewport === 'above' &&

Reference element is above the viewport.

} * * ); * }; * ``` */ export const usePositionRelativeToViewport = () => { const [ positionRelativeToViewport, setPositionRelativeToViewport ] = useState( '' ); const referenceElementRef = useRef( null ); const intersectionObserver = useRef( new IntersectionObserver( ( entries ) => { if ( entries[ 0 ].isIntersecting ) { setPositionRelativeToViewport( 'visible' ); } else { setPositionRelativeToViewport( entries[ 0 ].boundingClientRect.top > 0 ? 'below' : 'above' ); } }, { threshold: [ 0, 0.5, 1 ] } ) ); useLayoutEffect( () => { const referenceElementNode = referenceElementRef.current; const observer = intersectionObserver.current; if ( referenceElementNode ) { observer.observe( referenceElementNode ); } return () => { observer.unobserve( referenceElementNode ); }; }, [] ); const referenceElement = (
); return [ referenceElement, positionRelativeToViewport ]; };