UNPKG

@ariakit/react-core

Version:

Ariakit React core

274 lines (246 loc) 9.54 kB
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); var _UI5KWBYVcjs = require('./UI5KWBYV.cjs'); var _RDDLXR5Tcjs = require('./RDDLXR5T.cjs'); var _WULEED4Qcjs = require('./WULEED4Q.cjs'); var _OZM4QA2Vcjs = require('./OZM4QA2V.cjs'); var _FDRJDQ5Ycjs = require('./FDRJDQ5Y.cjs'); var _7EQBAZ46cjs = require('./7EQBAZ46.cjs'); // src/portal/portal.tsx var _dom = require('@ariakit/core/utils/dom'); var _events = require('@ariakit/core/utils/events'); var _focus = require('@ariakit/core/utils/focus'); var _react = require('react'); var _reactdom = require('react-dom'); var _jsxruntime = require('react/jsx-runtime'); var TagName = "div"; function getRootElement(element) { return _dom.getDocument.call(void 0, element).body; } function getPortalElement(element, portalElement) { if (!portalElement) { return _dom.getDocument.call(void 0, element).createElement("div"); } if (typeof portalElement === "function") { return portalElement(element); } return portalElement; } function getRandomId(prefix = "id") { return `${prefix ? `${prefix}-` : ""}${Math.random().toString(36).slice(2, 8)}`; } function queueFocus(element) { queueMicrotask(() => { element == null ? void 0 : element.focus(); }); } var usePortal = _WULEED4Qcjs.createHook.call(void 0, function usePortal2(_a) { var _b = _a, { preserveTabOrder, preserveTabOrderAnchor, portalElement, portalRef, portal = true } = _b, props = _7EQBAZ46cjs.__objRest.call(void 0, _b, [ "preserveTabOrder", "preserveTabOrderAnchor", "portalElement", "portalRef", "portal" ]); const ref = _react.useRef.call(void 0, null); const refProp = _OZM4QA2Vcjs.useMergeRefs.call(void 0, ref, props.ref); const context = _react.useContext.call(void 0, _UI5KWBYVcjs.PortalContext); const [portalNode, setPortalNode] = _react.useState.call(void 0, null); const [anchorPortalNode, setAnchorPortalNode] = _react.useState.call(void 0, null ); const outerBeforeRef = _react.useRef.call(void 0, null); const innerBeforeRef = _react.useRef.call(void 0, null); const innerAfterRef = _react.useRef.call(void 0, null); const outerAfterRef = _react.useRef.call(void 0, null); _OZM4QA2Vcjs.useSafeLayoutEffect.call(void 0, () => { const element = ref.current; if (!element || !portal) { setPortalNode(null); return; } const portalEl = getPortalElement(element, portalElement); if (!portalEl) { setPortalNode(null); return; } const isPortalInDocument = portalEl.isConnected; if (!isPortalInDocument) { const rootElement = context || getRootElement(element); rootElement.appendChild(portalEl); } if (!portalEl.id) { portalEl.id = element.id ? `portal/${element.id}` : getRandomId(); } setPortalNode(portalEl); _FDRJDQ5Ycjs.setRef.call(void 0, portalRef, portalEl); if (isPortalInDocument) return; return () => { portalEl.remove(); _FDRJDQ5Ycjs.setRef.call(void 0, portalRef, null); }; }, [portal, portalElement, context, portalRef]); _OZM4QA2Vcjs.useSafeLayoutEffect.call(void 0, () => { if (!portal) return; if (!preserveTabOrder) return; if (!preserveTabOrderAnchor) return; const doc = _dom.getDocument.call(void 0, preserveTabOrderAnchor); const element = doc.createElement("span"); element.style.position = "fixed"; preserveTabOrderAnchor.insertAdjacentElement("afterend", element); setAnchorPortalNode(element); return () => { element.remove(); setAnchorPortalNode(null); }; }, [portal, preserveTabOrder, preserveTabOrderAnchor]); _react.useEffect.call(void 0, () => { if (!portalNode) return; if (!preserveTabOrder) return; let raf = 0; const onFocus = (event) => { if (!_events.isFocusEventOutside.call(void 0, event)) return; const focusing = event.type === "focusin"; cancelAnimationFrame(raf); if (focusing) { return _focus.restoreFocusIn.call(void 0, portalNode); } raf = requestAnimationFrame(() => { _focus.disableFocusIn.call(void 0, portalNode, true); }); }; portalNode.addEventListener("focusin", onFocus, true); portalNode.addEventListener("focusout", onFocus, true); return () => { cancelAnimationFrame(raf); portalNode.removeEventListener("focusin", onFocus, true); portalNode.removeEventListener("focusout", onFocus, true); }; }, [portalNode, preserveTabOrder]); props = _OZM4QA2Vcjs.useWrapElement.call(void 0, props, (element) => { element = // While the portal node is not in the DOM, we need to pass the // current context to the portal context, otherwise it's going to // reset to the body element on nested portals. /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _UI5KWBYVcjs.PortalContext.Provider, { value: portalNode || context, children: element }); if (!portal) return element; if (!portalNode) { return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { ref: refProp, id: props.id, style: { position: "fixed" }, hidden: true } ); } element = /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [ preserveTabOrder && portalNode && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _RDDLXR5Tcjs.FocusTrap, { ref: innerBeforeRef, "data-focus-trap": props.id, className: "__focus-trap-inner-before", onFocus: (event) => { if (_events.isFocusEventOutside.call(void 0, event, portalNode)) { queueFocus(_focus.getNextTabbable.call(void 0, )); } else { queueFocus(outerBeforeRef.current); } } } ), element, preserveTabOrder && portalNode && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _RDDLXR5Tcjs.FocusTrap, { ref: innerAfterRef, "data-focus-trap": props.id, className: "__focus-trap-inner-after", onFocus: (event) => { if (_events.isFocusEventOutside.call(void 0, event, portalNode)) { queueFocus(_focus.getPreviousTabbable.call(void 0, )); } else { queueFocus(outerAfterRef.current); } } } ) ] }); if (portalNode) { element = _reactdom.createPortal.call(void 0, element, portalNode); } let preserveTabOrderElement = /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [ preserveTabOrder && portalNode && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _RDDLXR5Tcjs.FocusTrap, { ref: outerBeforeRef, "data-focus-trap": props.id, className: "__focus-trap-outer-before", onFocus: (event) => { const fromOuter = event.relatedTarget === outerAfterRef.current; if (!fromOuter && _events.isFocusEventOutside.call(void 0, event, portalNode)) { queueFocus(innerBeforeRef.current); } else { queueFocus(_focus.getPreviousTabbable.call(void 0, )); } } } ), preserveTabOrder && // We're using position: fixed here so that the browser doesn't // add margin to the element when setting gap on a parent element. /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { "aria-owns": portalNode == null ? void 0 : portalNode.id, style: { position: "fixed" } }), preserveTabOrder && portalNode && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _RDDLXR5Tcjs.FocusTrap, { ref: outerAfterRef, "data-focus-trap": props.id, className: "__focus-trap-outer-after", onFocus: (event) => { if (_events.isFocusEventOutside.call(void 0, event, portalNode)) { queueFocus(innerAfterRef.current); } else { const nextTabbable = _focus.getNextTabbable.call(void 0, ); if (nextTabbable === innerBeforeRef.current) { requestAnimationFrame(() => { var _a2; return (_a2 = _focus.getNextTabbable.call(void 0, )) == null ? void 0 : _a2.focus(); }); return; } queueFocus(nextTabbable); } } } ) ] }); if (anchorPortalNode && preserveTabOrder) { preserveTabOrderElement = _reactdom.createPortal.call(void 0, preserveTabOrderElement, anchorPortalNode ); } return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [ preserveTabOrderElement, element ] }); }, [portalNode, context, portal, props.id, preserveTabOrder, anchorPortalNode] ); props = _7EQBAZ46cjs.__spreadProps.call(void 0, _7EQBAZ46cjs.__spreadValues.call(void 0, {}, props), { ref: refProp }); return props; }); var Portal = _WULEED4Qcjs.forwardRef.call(void 0, function Portal2(props) { const htmlProps = usePortal(props); return _WULEED4Qcjs.createElement.call(void 0, TagName, htmlProps); }); exports.usePortal = usePortal; exports.Portal = Portal;