mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2025-12-30 11:22:31 +00:00
Currently the clientWidth is not representing the window width but it is representing the available video space width since we are subtracting the width of the participants pane and chat area.
127 lines
3.6 KiB
TypeScript
127 lines
3.6 KiB
TypeScript
import { ReactNode, useEffect, useRef, useState } from 'react';
|
|
import ReactDOM from 'react-dom';
|
|
import { useSelector } from 'react-redux';
|
|
|
|
import { IReduxState } from '../../../app/types';
|
|
import { debounce } from '../../../base/config/functions.any';
|
|
import { ZINDEX_DIALOG_PORTAL } from '../../constants';
|
|
|
|
interface IProps {
|
|
|
|
/**
|
|
* The component(s) to be displayed within the drawer portal.
|
|
*/
|
|
children: ReactNode;
|
|
|
|
/**
|
|
* Custom class name to apply on the container div.
|
|
*/
|
|
className?: string;
|
|
|
|
/**
|
|
* Function used to get the reference to the container div.
|
|
*/
|
|
getRef?: Function;
|
|
|
|
/**
|
|
* Function called when the portal target becomes actually visible.
|
|
*/
|
|
onVisible?: Function;
|
|
|
|
/**
|
|
* Function used to get the updated size info of the container on it's resize.
|
|
*/
|
|
setSize?: Function;
|
|
|
|
/**
|
|
* Custom style to apply to the container div.
|
|
*/
|
|
style?: any;
|
|
|
|
/**
|
|
* The selector for the element we consider the content container.
|
|
* This is used to determine the correct size of the portal content.
|
|
*/
|
|
targetSelector?: string;
|
|
}
|
|
|
|
/**
|
|
* 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, targetSelector, onVisible }: IProps) {
|
|
const videoSpaceWidth = useSelector((state: IReduxState) => state['features/base/responsive-ui'].videoSpaceWidth);
|
|
const [ portalTarget ] = useState(() => {
|
|
const portalDiv = document.createElement('div');
|
|
|
|
portalDiv.style.visibility = 'hidden';
|
|
|
|
return portalDiv;
|
|
});
|
|
const timerRef = useRef<number>();
|
|
|
|
useEffect(() => {
|
|
if (style) {
|
|
for (const styleProp of Object.keys(style)) {
|
|
const objStyle: any = portalTarget.style;
|
|
|
|
objStyle[styleProp] = style[styleProp];
|
|
}
|
|
}
|
|
if (className) {
|
|
portalTarget.className = className;
|
|
}
|
|
}, [ style, className ]);
|
|
|
|
useEffect(() => {
|
|
if (portalTarget && getRef) {
|
|
getRef(portalTarget);
|
|
portalTarget.style.zIndex = `${ZINDEX_DIALOG_PORTAL}`;
|
|
}
|
|
}, [ portalTarget, getRef ]);
|
|
|
|
useEffect(() => {
|
|
const size = {
|
|
width: 1,
|
|
height: 1
|
|
};
|
|
const debouncedResizeCallback = debounce((entries: ResizeObserverEntry[]) => {
|
|
const { contentRect } = entries[0];
|
|
|
|
if (contentRect.width !== size.width || contentRect.height !== size.height) {
|
|
setSize?.(contentRect);
|
|
clearTimeout(timerRef.current);
|
|
timerRef.current = window.setTimeout(() => {
|
|
portalTarget.style.visibility = 'visible';
|
|
onVisible?.();
|
|
}, 100);
|
|
}
|
|
}, 20); // 20ms delay
|
|
|
|
// Create and observe ResizeObserver
|
|
const observer = new ResizeObserver(debouncedResizeCallback);
|
|
const target = targetSelector ? portalTarget.querySelector(targetSelector) : portalTarget;
|
|
|
|
if (document.body) {
|
|
document.body.appendChild(portalTarget);
|
|
observer.observe(target ?? portalTarget);
|
|
}
|
|
|
|
return () => {
|
|
observer.unobserve(target ?? portalTarget);
|
|
if (document.body) {
|
|
document.body.removeChild(portalTarget);
|
|
}
|
|
};
|
|
}, [ videoSpaceWidth ]);
|
|
|
|
return ReactDOM.createPortal(
|
|
children,
|
|
portalTarget
|
|
);
|
|
}
|
|
|
|
export default DialogPortal;
|