UNPKG

@gluestack-ui-nightly/core

Version:

Universal UI components for React Native, Expo, and Next.js

113 lines 4.14 kB
import { calculatePosition } from './calculatePosition'; import { useCallback, useRef, useState, } from 'react'; import { useCloseOnScroll } from './useCloseOnScroll'; import { isRTL, useLayoutEffect } from '@gluestack-ui-nightly/utils/aria'; let visualViewport = typeof window !== 'undefined' && window.visualViewport; export function useOverlayPosition(props) { var _a, _b; const direction = isRTL() ? 'rtl' : undefined; let { targetRef, overlayRef, scrollRef = overlayRef, placement = 'bottom', containerPadding = 12, shouldFlip = true, boundaryElement = typeof document !== 'undefined' ? document.body : null, offset = 0, crossOffset = 0, shouldUpdatePosition = true, isOpen = true, shouldOverlapWithTrigger = false, onClose, } = props; let [position, setPosition] = useState({ position: {}, arrowOffsetLeft: undefined, arrowOffsetTop: undefined, maxHeight: undefined, placement: undefined, }); let deps = [ shouldUpdatePosition, placement, overlayRef.current, (_a = targetRef.current) === null || _a === void 0 ? void 0 : _a.offsetLeft, (_b = targetRef.current) === null || _b === void 0 ? void 0 : _b.offsetTop, scrollRef.current, containerPadding, shouldFlip, boundaryElement, offset, crossOffset, isOpen, direction, shouldOverlapWithTrigger, ]; let updatePosition = useCallback(() => { if (shouldUpdatePosition === false || !isOpen || !overlayRef.current || !targetRef.current || !scrollRef.current || !boundaryElement) { return; } setPosition(calculatePosition({ placement: translateRTL(placement, direction), overlayNode: overlayRef.current, targetNode: targetRef.current, scrollNode: scrollRef.current, padding: containerPadding, shouldFlip, boundaryElement, offset, crossOffset, shouldOverlapWithTrigger, })); }, deps); useLayoutEffect(updatePosition, deps); useResize(updatePosition); let isResizing = useRef(false); useLayoutEffect(() => { let timeout; let onResize = () => { isResizing.current = true; clearTimeout(timeout); timeout = setTimeout(() => { isResizing.current = false; }, 500); updatePosition(); }; visualViewport === null || visualViewport === void 0 ? void 0 : visualViewport.addEventListener('resize', onResize); return () => { visualViewport === null || visualViewport === void 0 ? void 0 : visualViewport.removeEventListener('resize', onResize); }; }, [updatePosition]); let close = useCallback(() => { if (!isResizing.current) { onClose(); } }, [onClose, isResizing]); useCloseOnScroll({ triggerRef: targetRef, isOpen, onClose: onClose ? close : undefined, }); return { rendered: true, overlayProps: { style: Object.assign(Object.assign({ position: 'absolute', zIndex: 100000 }, position.position), { maxHeight: position.maxHeight }), }, placement: position.placement, arrowProps: { style: { left: position.arrowOffsetLeft, top: position.arrowOffsetTop, }, }, isFlipped: position.isFlipped, updatePosition, }; } function useResize(onResize) { useLayoutEffect(() => { window.addEventListener('resize', onResize, false); return () => { window.removeEventListener('resize', onResize, false); }; }, [onResize]); } function translateRTL(position, direction) { if (direction === 'rtl') { return position.replace('start', 'right').replace('end', 'left'); } return position.replace('start', 'left').replace('end', 'right'); } //# sourceMappingURL=useOverlayPosition.js.map