Popover.tsx 1.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
  1. import React, { useLayoutEffect, useRef } from "react";
  2. import "./Popover.css";
  3. type Props = {
  4. top?: number;
  5. left?: number;
  6. children?: React.ReactNode;
  7. onCloseRequest?(): void;
  8. fitInViewport?: boolean;
  9. };
  10. export function Popover({
  11. children,
  12. left,
  13. top,
  14. onCloseRequest,
  15. fitInViewport = false,
  16. }: Props) {
  17. const popoverRef = useRef<HTMLDivElement>(null);
  18. // ensure the popover doesn't overflow the viewport
  19. useLayoutEffect(() => {
  20. if (fitInViewport && popoverRef.current) {
  21. const element = popoverRef.current;
  22. const { x, y, width, height } = element.getBoundingClientRect();
  23. const viewportWidth = window.innerWidth;
  24. if (x + width > viewportWidth) {
  25. element.style.left = `${viewportWidth - width}px`;
  26. }
  27. const viewportHeight = window.innerHeight;
  28. if (y + height > viewportHeight) {
  29. element.style.top = `${viewportHeight - height}px`;
  30. }
  31. }
  32. }, [fitInViewport]);
  33. return (
  34. <div className="popover" style={{ top: top, left: left }} ref={popoverRef}>
  35. <div
  36. className="cover"
  37. onClick={onCloseRequest}
  38. onContextMenu={event => {
  39. event.preventDefault();
  40. if (onCloseRequest) {
  41. onCloseRequest();
  42. }
  43. }}
  44. />
  45. {children}
  46. </div>
  47. );
  48. }