Modal.tsx 1.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
  1. import "./Modal.css";
  2. import React, { useEffect, useState } from "react";
  3. import { createPortal } from "react-dom";
  4. import { KEYS } from "../keys";
  5. export function Modal(props: {
  6. children: React.ReactNode;
  7. maxWidth?: number;
  8. onCloseRequest(): void;
  9. labelledBy: string;
  10. }) {
  11. const modalRoot = useBodyRoot();
  12. const handleKeydown = (event: React.KeyboardEvent) => {
  13. if (event.key === KEYS.ESCAPE) {
  14. event.nativeEvent.stopImmediatePropagation();
  15. props.onCloseRequest();
  16. }
  17. };
  18. return createPortal(
  19. <div
  20. className="Modal"
  21. role="dialog"
  22. aria-modal="true"
  23. onKeyDown={handleKeydown}
  24. aria-labelledby={props.labelledBy}
  25. >
  26. <div className="Modal__background" onClick={props.onCloseRequest}></div>
  27. <div className="Modal__content" style={{ maxWidth: props.maxWidth }}>
  28. {props.children}
  29. </div>
  30. </div>,
  31. modalRoot,
  32. );
  33. }
  34. function useBodyRoot() {
  35. function createDiv() {
  36. const div = document.createElement("div");
  37. document.body.appendChild(div);
  38. return div;
  39. }
  40. const [div] = useState(createDiv);
  41. useEffect(() => {
  42. return () => {
  43. document.body.removeChild(div);
  44. };
  45. }, [div]);
  46. return div;
  47. }