UNPKG

@dnb/eufemia

Version:

DNB Eufemia Design System UI Library

131 lines (130 loc) 4.4 kB
"use client"; import _extends from "@babel/runtime-corejs3/helpers/esm/extends"; import React, { useContext, useMemo, useReducer, useRef } from 'react'; import { createPortal } from 'react-dom'; import classnames from 'classnames'; import IsolatedStyleScope, { IsolatedStyleScopeContext } from "../../shared/IsolatedStyleScope.js"; import { useIsomorphicLayoutEffect as useLayoutEffect } from "../../shared/helpers/useIsomorphicLayoutEffect.js"; const PortalRootContext = React.createContext(null); export function PortalRootProvider(props) { const { id, insideSelector, beforeSelector, children } = props; const value = useMemo(() => ({ id, insideSelector, beforeSelector }), [id, insideSelector, beforeSelector]); return React.createElement(PortalRootContext.Provider, { value: value }, children); } function PortalRoot(props = {}) { var _ref; const { id: idProp, insideSelector: insideSelectorProp, beforeSelector: beforeSelectorProp, innerRef, className, style, children, ...rest } = props; const [, forceUpdate] = useReducer(() => ({}), {}); const { style: scopeStyle } = useContext(IsolatedStyleScopeContext) || {}; const selectorContext = useContext(PortalRootContext); const hasPropOverride = typeof insideSelectorProp !== 'undefined' || typeof beforeSelectorProp !== 'undefined'; const insideSelector = hasPropOverride ? insideSelectorProp : selectorContext === null || selectorContext === void 0 ? void 0 : selectorContext.insideSelector; const beforeSelector = hasPropOverride ? beforeSelectorProp : selectorContext === null || selectorContext === void 0 ? void 0 : selectorContext.beforeSelector; const effectiveId = (_ref = idProp !== null && idProp !== void 0 ? idProp : selectorContext === null || selectorContext === void 0 ? void 0 : selectorContext.id) !== null && _ref !== void 0 ? _ref : 'eufemia-portal-root'; const initialElement = useMemo(() => { if (idProp && typeof document !== 'undefined') { return document.getElementById(idProp); } return getOrCreatePortalElement({ id: effectiveId, insideSelector, beforeSelector }); }, [effectiveId, idProp, insideSelector, beforeSelector]); const localRef = useRef(initialElement); useLayoutEffect(() => { if (idProp || insideSelector || beforeSelector) { const elem = getOrCreatePortalElement({ id: effectiveId, insideSelector, beforeSelector }); if (localRef.current !== elem) { localRef.current = elem; forceUpdate(); } } if (innerRef && localRef.current) { if (typeof innerRef === 'function') { innerRef(localRef.current); } else { const ref = innerRef; ref.current = localRef.current; } } }, [effectiveId, idProp, innerRef, insideSelector, beforeSelector]); const portalElement = localRef.current || initialElement; if (!portalElement) { return null; } return createPortal(React.createElement(IsolatedStyleScope, { scopeHash: "auto", disableCoreStyleWrapper: true, uniqueKey: false }, React.createElement("div", _extends({ className: classnames("dnb-core-style eufemia-portal-root", className), style: { ...scopeStyle, ...style } }, rest), children)), portalElement); } export function getOrCreatePortalElement({ id, insideSelector, beforeSelector }) { if (typeof document === 'undefined') { return null; } let elem = document.getElementById(id); if (!elem) { let parent = document.body; let referenceNode = document.body.firstChild; if (beforeSelector) { const target = document.querySelector(beforeSelector); if (target && target.parentElement) { parent = target.parentElement; referenceNode = target; } } else if (insideSelector) { const target = document.querySelector(insideSelector); if (target) { parent = target; referenceNode = target.firstChild; } } elem = document.createElement('div'); elem.setAttribute('id', id); parent.insertBefore(elem, referenceNode); } if (!elem.hasAttribute('role')) { elem.setAttribute('role', 'presentation'); } return elem; } PortalRoot.Provider = PortalRootProvider; export default PortalRoot; //# sourceMappingURL=PortalRoot.js.map