UNPKG

@zohodesk/components

Version:

In this Package, we Provide Some Basic Components to Build Web App

125 lines (110 loc) 4.01 kB
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;