Files
cunningham/packages/react/src/components/Popover/index.tsx
Anthony Le Courric 9edb9765db (react) position top datepicker
Position the datepicker on top of the input when the input is
at the bottom of the screen and whenthere is enough space to display
the datepicker on the top.
2023-08-21 12:32:14 +02:00

76 lines
2.0 KiB
TypeScript

import React, {
PropsWithChildren,
RefObject,
useLayoutEffect,
useRef,
useState,
} from "react";
import classNames from "classnames";
import { useHandleClickOutside } from ":/hooks/useHandleClickOutside";
type PopoverProps = PropsWithChildren & {
parentRef: RefObject<HTMLDivElement>;
onClickOutside: () => void;
borderless?: boolean;
};
export const Popover = ({
parentRef,
children,
onClickOutside,
borderless = false,
}: PopoverProps) => {
const popoverRef = useRef<HTMLDivElement>(null);
useHandleClickOutside(parentRef, onClickOutside);
const timeout = useRef<ReturnType<typeof setTimeout>>();
const [topPosition, setTopPosition] = useState<number | undefined>();
useLayoutEffect(() => {
const setPopoverTopPosition = () => {
if (!parentRef.current || !popoverRef.current) return;
const parentBounds = parentRef.current.getBoundingClientRect();
const popoverBounds = popoverRef.current.getBoundingClientRect();
const hasNotEnoughBottomPlace =
window.innerHeight - parentBounds.bottom < popoverBounds.height;
const hasEnoughTopPlace = parentBounds.top >= popoverBounds.height;
if (hasNotEnoughBottomPlace && hasEnoughTopPlace) {
setTopPosition(-popoverBounds.height);
} else {
setTopPosition(undefined);
}
};
const handleWindowResize = () => {
if (timeout.current) clearTimeout(timeout.current);
timeout.current = setTimeout(setPopoverTopPosition, 1000 / 30);
};
window.addEventListener("resize", handleWindowResize);
setPopoverTopPosition();
return () => {
window.removeEventListener("resize", handleWindowResize);
if (timeout.current) clearTimeout(timeout.current);
};
}, []);
return (
<div
ref={popoverRef}
className={classNames("c__popover", {
"c__popover--borderless": borderless,
})}
style={{
top: topPosition,
}}
role="dialog"
>
{children}
</div>
);
};