woocommerce/plugins/woocommerce-blocks/assets/js/base/hooks/use-position-relative-to-vi...

87 lines
2.1 KiB
JavaScript

/**
* 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' && <p>Reference element is below the viewport.</p> }
* { positionRelativeToViewport === 'visible' && <p>Reference element is visible in the viewport.</p> }
* { positionRelativeToViewport === 'above' && <p>Reference element is above the viewport.</p> }
* </>
* );
* };
* ```
*/
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: 1.0 }
)
);
useLayoutEffect( () => {
const referenceElementNode = referenceElementRef.current;
const observer = intersectionObserver.current;
if ( referenceElementNode ) {
observer.observe( referenceElementNode );
}
return () => {
observer.unobserve( referenceElementNode );
};
}, [] );
const referenceElement = (
<div aria-hidden={ true } ref={ referenceElementRef } style={ style } />
);
return [ referenceElement, positionRelativeToViewport ];
};