You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DialogPortal.js 2.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. // @flow
  2. import { useEffect, useState } from 'react';
  3. import ReactDOM from 'react-dom';
  4. type Props = {
  5. /**
  6. * The component(s) to be displayed within the drawer portal.
  7. */
  8. children: React$Node,
  9. /**
  10. * Custom class name to apply on the container div.
  11. */
  12. className?: string,
  13. /**
  14. * Function used to get the refferrence to the container div.
  15. */
  16. getRef?: Function,
  17. /**
  18. * Function used to get the updated size info of the container on it's resize.
  19. */
  20. setSize?: Function,
  21. /**
  22. * Custom style to apply to the container div.
  23. */
  24. style?: Object,
  25. };
  26. /**
  27. * Component meant to render a drawer at the bottom of the screen,
  28. * by creating a portal containing the component's children.
  29. *
  30. * @returns {ReactElement}
  31. */
  32. function DialogPortal({ children, className, style, getRef, setSize }: Props) {
  33. const [ portalTarget ] = useState(() => {
  34. const portalDiv = document.createElement('div');
  35. return portalDiv;
  36. });
  37. useEffect(() => {
  38. if (style) {
  39. for (const styleProp of Object.keys(style)) {
  40. // https://github.com/facebook/flow/issues/3733
  41. const objStyle: Object = portalTarget.style;
  42. objStyle[styleProp] = style[styleProp];
  43. }
  44. }
  45. if (className) {
  46. portalTarget.className = className;
  47. }
  48. }, [ style, className ]);
  49. useEffect(() => {
  50. if (portalTarget && getRef) {
  51. getRef(portalTarget);
  52. }
  53. }, [ portalTarget ]);
  54. useEffect(() => {
  55. const size = {
  56. width: 1,
  57. height: 1
  58. };
  59. const observer = new ResizeObserver(entries => {
  60. const { contentRect } = entries[0];
  61. if (contentRect.width !== size.width || contentRect.height !== size.height) {
  62. setSize && setSize(contentRect);
  63. }
  64. });
  65. if (document.body) {
  66. document.body.appendChild(portalTarget);
  67. observer.observe(portalTarget);
  68. }
  69. return () => {
  70. observer.unobserve(portalTarget);
  71. if (document.body) {
  72. document.body.removeChild(portalTarget);
  73. }
  74. };
  75. }, []);
  76. return ReactDOM.createPortal(
  77. children,
  78. portalTarget
  79. );
  80. }
  81. export default DialogPortal;