@reach/dialog
Version:
Accessible React Modal Dialog.
207 lines (204 loc) • 6.29 kB
JavaScript
/**
* @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
};
;