UNPKG

@reach/dialog

Version:

Accessible React Modal Dialog.

207 lines (204 loc) 6.29 kB
"use strict"; /** * @reach/dialog v0.18.0 * * Copyright (c) 2018-2022, React Training LLC * * This source code is licensed under the MIT license found in the * LICENSE.md file in the root directory of this source tree. * * @license MIT */ // src/reach-dialog.tsx import * as React from "react"; import { Portal } from "@reach/portal"; import { composeEventHandlers, createContext, getOwnerDocument, noop, useComposedRefs } from "@reach/utils"; import FocusLock from "react-focus-lock"; import { RemoveScroll } from "react-remove-scroll"; var [DialogContextProvider, useDialogContext] = createContext("DialogContext", { isOpen: false }); function DialogWrapper({ isOpen = true, children, ...props }) { React.useEffect(() => { if (isOpen) { window.__REACH_DISABLE_TOOLTIPS = true; } else { window.requestAnimationFrame(() => { window.__REACH_DISABLE_TOOLTIPS = false; }); } }, [isOpen]); return /* @__PURE__ */ React.createElement(Portal, { "data-reach-dialog-wrapper": "", "data-state": isOpen ? "open" /* Open */ : "closed" /* Closed */, ...props }, /* @__PURE__ */ React.createElement(DialogContextProvider, { isOpen }, children)); } DialogWrapper.displayName = "unstable_DialogWrapper"; var DialogInner = React.forwardRef(function DialogInner2({ allowPinchZoom, as: Comp = "div", dangerouslyBypassFocusLock, dangerouslyBypassScrollLock, initialFocusRef, onClick, onDismiss = noop, onKeyDown, onMouseDown, unstable_lockFocusAcrossFrames, ...props }, forwardedRef) { let { isOpen } = useDialogContext("DialogInner"); let lockFocusAcrossFramesIsDefined = unstable_lockFocusAcrossFrames !== void 0; if (true) { React.useEffect(() => { if (lockFocusAcrossFramesIsDefined) { console.warn(`The unstable_lockFocusAcrossFrames in @reach/dialog is deprecated. It will be removed in the next minor release.`); } }, [lockFocusAcrossFramesIsDefined]); } const mouseDownTarget = React.useRef(null); const overlayNode = React.useRef(null); const ref = useComposedRefs(overlayNode, forwardedRef); const activateFocusLock = React.useCallback(() => { if (initialFocusRef && initialFocusRef.current) { initialFocusRef.current.focus(); } }, [initialFocusRef]); function handleClick(event) { if (mouseDownTarget.current === event.target) { event.stopPropagation(); onDismiss(event); } } function handleKeyDown(event) { if (event.key === "Escape") { event.stopPropagation(); onDismiss(event); } } function handleMouseDown(event) { mouseDownTarget.current = event.target; } React.useEffect(() => { return overlayNode.current ? createAriaHider(overlayNode.current) : void 0; }, []); return /* @__PURE__ */ React.createElement(FocusLock, { autoFocus: true, returnFocus: true, onActivation: activateFocusLock, disabled: dangerouslyBypassFocusLock != null ? dangerouslyBypassFocusLock : !isOpen, crossFrame: unstable_lockFocusAcrossFrames ?? true }, /* @__PURE__ */ React.createElement(RemoveScroll, { allowPinchZoom, enabled: dangerouslyBypassScrollLock != null ? !dangerouslyBypassScrollLock : isOpen }, /* @__PURE__ */ React.createElement(Comp, { ...props, ref, "data-reach-dialog-inner": "", "data-state": isOpen ? "open" /* Open */ : "closed" /* Closed */, onClick: composeEventHandlers(onClick, handleClick), onKeyDown: composeEventHandlers(onKeyDown, handleKeyDown), onMouseDown: composeEventHandlers(onMouseDown, handleMouseDown) }))); }); DialogInner.displayName = "DialogInner"; var DialogOverlay = React.forwardRef(function DialogOverlay2({ as: Comp = "div", isOpen = true, ...props }, forwardedRef) { return isOpen ? /* @__PURE__ */ React.createElement(DialogWrapper, { isOpen }, /* @__PURE__ */ React.createElement(DialogInner, { "data-reach-dialog-overlay": "", ref: forwardedRef, as: Comp, ...props })) : null; }); DialogOverlay.displayName = "DialogOverlay"; var DialogContent = React.forwardRef(function DialogContent2({ as: Comp = "div", onClick, onKeyDown, ...props }, forwardedRef) { let { isOpen } = useDialogContext("DialogContent"); return /* @__PURE__ */ React.createElement(Comp, { "aria-modal": "true", role: "dialog", tabIndex: -1, ...props, ref: forwardedRef, "data-reach-dialog-content": "", "data-state": isOpen ? "open" /* Open */ : "closed" /* Closed */, onClick: composeEventHandlers(onClick, (event) => { event.stopPropagation(); }) }); }); DialogContent.displayName = "DialogContent"; var Dialog = React.forwardRef(function Dialog2({ allowPinchZoom = false, initialFocusRef, isOpen, onDismiss = noop, ...props }, forwardedRef) { return /* @__PURE__ */ React.createElement(DialogOverlay, { allowPinchZoom, initialFocusRef, isOpen, onDismiss }, /* @__PURE__ */ React.createElement(DialogContent, { ref: forwardedRef, ...props })); }); Dialog.displayName = "Dialog"; function createAriaHider(dialogNode) { let originalValues = []; let rootNodes = []; let ownerDocument = getOwnerDocument(dialogNode); if (!dialogNode) { if (true) { console.warn("A ref has not yet been attached to a dialog node when attempting to call `createAriaHider`."); } return noop; } Array.prototype.forEach.call(ownerDocument.querySelectorAll("body > *"), (node) => { const portalNode = dialogNode.parentNode?.parentNode?.parentNode; if (node === portalNode) { return; } let attr = node.getAttribute("aria-hidden"); let alreadyHidden = attr !== null && attr !== "false"; if (alreadyHidden) { return; } originalValues.push(attr); rootNodes.push(node); node.setAttribute("aria-hidden", "true"); }); return () => { rootNodes.forEach((node, index) => { let originalValue = originalValues[index]; if (originalValue === null) { node.removeAttribute("aria-hidden"); } else { node.setAttribute("aria-hidden", originalValue); } }); }; } export { Dialog, DialogContent, DialogInner, DialogOverlay, DialogWrapper as unstable_DialogWrapper };