forked from github/plane
65 lines
1.9 KiB
TypeScript
65 lines
1.9 KiB
TypeScript
|
import React, { useCallback, useEffect } from "react";
|
||
|
|
||
|
// hook
|
||
|
import useOutsideClickDetector from "./use-outside-click-detector";
|
||
|
|
||
|
/**
|
||
|
* Custom hook for dynamic dropdown position calculation.
|
||
|
* @param isOpen - Indicates whether the dropdown is open.
|
||
|
* @param handleClose - Callback to handle closing the dropdown.
|
||
|
* @param buttonRef - Ref object for the button triggering the dropdown.
|
||
|
* @param dropdownRef - Ref object for the dropdown element.
|
||
|
*/
|
||
|
|
||
|
const useDynamicDropdownPosition = (
|
||
|
isOpen: boolean,
|
||
|
handleClose: () => void,
|
||
|
buttonRef: React.RefObject<any>,
|
||
|
dropdownRef: React.RefObject<any>
|
||
|
) => {
|
||
|
const handlePosition = useCallback(() => {
|
||
|
const button = buttonRef.current;
|
||
|
const dropdown = dropdownRef.current;
|
||
|
|
||
|
if (!dropdown || !button) return;
|
||
|
|
||
|
const buttonRect = button.getBoundingClientRect();
|
||
|
const dropdownRect = dropdown.getBoundingClientRect();
|
||
|
|
||
|
const { innerHeight, innerWidth, scrollX, scrollY } = window;
|
||
|
|
||
|
let top: number = buttonRect.bottom + scrollY;
|
||
|
if (top + dropdownRect.height > innerHeight) top = innerHeight - dropdownRect.height;
|
||
|
|
||
|
let left: number = buttonRect.left + scrollX + (buttonRect.width - dropdownRect.width) / 2;
|
||
|
if (left + dropdownRect.width > innerWidth) left = innerWidth - dropdownRect.width;
|
||
|
|
||
|
dropdown.style.top = `${Math.max(top, 5)}px`;
|
||
|
dropdown.style.left = `${Math.max(left, 5)}px`;
|
||
|
}, [buttonRef, dropdownRef]);
|
||
|
|
||
|
useEffect(() => {
|
||
|
if (isOpen) handlePosition();
|
||
|
}, [handlePosition, isOpen]);
|
||
|
|
||
|
useOutsideClickDetector(dropdownRef, () => {
|
||
|
if (isOpen) handleClose();
|
||
|
});
|
||
|
|
||
|
const handleResize = useCallback(() => {
|
||
|
if (isOpen) {
|
||
|
handlePosition();
|
||
|
}
|
||
|
}, [handlePosition, isOpen]);
|
||
|
|
||
|
useEffect(() => {
|
||
|
window.addEventListener("resize", handleResize);
|
||
|
|
||
|
return () => {
|
||
|
window.removeEventListener("resize", handleResize);
|
||
|
};
|
||
|
}, [isOpen, handleResize]);
|
||
|
};
|
||
|
|
||
|
export default useDynamicDropdownPosition;
|