jiti-meet/react/features/toolbox/components/web/DialogPortal.ts

104 lines
2.5 KiB
TypeScript
Raw Normal View History

2023-02-02 11:12:31 +00:00
import { ReactNode, useEffect, useRef, useState } from 'react';
import ReactDOM from 'react-dom';
type Props = {
/**
* The component(s) to be displayed within the drawer portal.
*/
2023-02-02 11:12:31 +00:00
children: ReactNode;
/**
* Custom class name to apply on the container div.
*/
2023-02-02 11:12:31 +00:00
className?: string;
/**
2023-02-02 11:12:31 +00:00
* Function used to get the reference to the container div.
*/
2023-02-02 11:12:31 +00:00
getRef?: Function;
/**
* Function used to get the updated size info of the container on it's resize.
*/
2023-02-02 11:12:31 +00:00
setSize?: Function;
/**
* Custom style to apply to the container div.
*/
2023-02-02 11:12:31 +00:00
style?: any;
};
/**
* Component meant to render a drawer at the bottom of the screen,
* by creating a portal containing the component's children.
*
* @returns {ReactElement}
*/
function DialogPortal({ children, className, style, getRef, setSize }: Props) {
const [ portalTarget ] = useState(() => {
const portalDiv = document.createElement('div');
portalDiv.style.visibility = 'hidden';
return portalDiv;
});
2023-02-02 11:12:31 +00:00
const timerRef = useRef<number>();
useEffect(() => {
if (style) {
for (const styleProp of Object.keys(style)) {
2023-02-02 11:12:31 +00:00
const objStyle: any = portalTarget.style;
objStyle[styleProp] = style[styleProp];
}
}
if (className) {
portalTarget.className = className;
}
}, [ style, className ]);
useEffect(() => {
if (portalTarget && getRef) {
getRef(portalTarget);
}
}, [ portalTarget ]);
useEffect(() => {
const size = {
width: 1,
height: 1
};
const observer = new ResizeObserver(entries => {
const { contentRect } = entries[0];
if (contentRect.width !== size.width || contentRect.height !== size.height) {
2023-02-02 11:12:31 +00:00
setSize?.(contentRect);
clearTimeout(timerRef.current);
2023-02-02 11:12:31 +00:00
timerRef.current = window.setTimeout(() => {
portalTarget.style.visibility = 'visible';
}, 100);
}
});
if (document.body) {
document.body.appendChild(portalTarget);
observer.observe(portalTarget);
}
return () => {
observer.unobserve(portalTarget);
if (document.body) {
document.body.removeChild(portalTarget);
}
};
}, []);
return ReactDOM.createPortal(
2023-02-02 11:12:31 +00:00
children,
portalTarget
);
}
export default DialogPortal;