Modal.tsx 1.2 KB

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