@zohodesk/components
Version:
In this Package, we Provide Some Basic Components to Build Web App
125 lines (110 loc) • 4.01 kB
JavaScript
import React, { useState, useRef, useEffect, useCallback } from 'react';
import ReactDOM from 'react-dom';
import useEffectCallOnlyAfterState from '@zohodesk/hooks/es/utils/useEffectCallOnlyAfterState';
import { defaultProps } from "./props/defaultProps";
import { propTypes } from "./props/propTypes";
import style from "../../AppContainer/AppContainer.module.css";
let createdPortalIds = [];
let newPortalPrefix = 'CPortal';
let portalChildrenTopIndexValues;
export default function Modal(props) {
let {
children,
isActive,
autoZIndexNeeded,
portalId: propPortalId
} = props;
const [isMounted, setMounted] = useState(false);
const portalId = useRef(propPortalId);
const ref = useRef(null);
const isZIndexAppendNeeded = useRef();
const zIndex = useRef();
const modalRoot = useRef();
const newModalRoot = useRef();
const containerDiv = useRef();
const portalDiv = useRef(); // document.createDocumentFragment()
const isCustomPortalId = useRef();
const setRef = useCallback(ele => {
ref.current = ele;
if (isZIndexAppendNeeded.current) {
ref.current.style.zIndex = zIndex.current;
isZIndexAppendNeeded.current = false;
}
}, []);
function getPortalDiv() {
modalRoot.current = propPortalId ? document.querySelector(`[data-portal=${propPortalId}]`) : "";
if (!modalRoot.current) {
portalId.current = handleAddPortalId();
newModalRoot.current = document.createElement('div');
newModalRoot.current.className = style.container;
newModalRoot.current.setAttribute('data-portal', portalId.current);
containerDiv.current && containerDiv.current.appendChild(newModalRoot.current);
modalRoot.current = newModalRoot.current;
}
return modalRoot.current;
}
useEffect(() => {
containerDiv.current = document.getElementsByTagName("desk")[0] ? document.getElementsByTagName('desk')[0] : document.getElementsByTagName('body')[0];
portalDiv.current = getPortalDiv();
setMounted(true);
setTimeout(() => {
handleInsertPortalDiv();
});
return () => {
// modalRoot.current && modalRoot.current.removeChild(portalDiv.current);
if (newModalRoot.current) {
containerDiv.current && containerDiv.current.removeChild(newModalRoot.current);
handleRemovePortalId();
}
if (zIndex.current && portalChildrenTopIndexValues === zIndex.current) {
portalChildrenTopIndexValues -= 1;
}
};
}, []);
useEffectCallOnlyAfterState(() => {
if (isActive) {
handleInsertPortalDiv();
} else if (zIndex.current && portalChildrenTopIndexValues === zIndex.current) {
portalChildrenTopIndexValues -= 1;
}
}, [isActive]);
function handleInsertPortalDiv() {
if (autoZIndexNeeded && isActive) {
let newHighValue = Number(portalChildrenTopIndexValues || 10) + 1;
portalChildrenTopIndexValues = newHighValue;
zIndex.current = newHighValue;
if (ref.current) {
ref.current.style.zIndex = newHighValue;
} else {
isZIndexAppendNeeded.current = true;
}
}
}
function handleAddPortalId() {
let createdPortalIdsLen = createdPortalIds.length;
let newPortalId = createdPortalIdsLen ? createdPortalIds[createdPortalIdsLen - 1] + 1 : 1;
createdPortalIds.push(newPortalId);
portalId.current = newPortalId;
isCustomPortalId.current = true;
return `${newPortalPrefix}${newPortalId}`;
}
function handleRemovePortalId() {
if (isCustomPortalId.current) {
createdPortalIds = createdPortalIds.filter(id => id !== portalId.current);
}
}
let Element = children.type,
elementProps = children.props;
if (isMounted) {
if (Element) {
return /*#__PURE__*/ReactDOM.createPortal( /*#__PURE__*/React.createElement(Element, {
ref: setRef,
...elementProps
}), portalDiv.current);
}
return null;
}
return null;
}
Modal.defaultProps = defaultProps;
Modal.propTypes = propTypes;