@gluestack-ui/core
Version:
Universal UI components for React Native, Expo, and Next.js
113 lines • 4.13 kB
JavaScript
import { calculatePosition } from './calculatePosition';
import { useCallback, useRef, useState, } from 'react';
import { useCloseOnScroll } from './useCloseOnScroll';
import { isRTL, useLayoutEffect } from '@gluestack-ui/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