@wordpress/components
Version:
UI components for WordPress.
284 lines (282 loc) • 12.1 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// packages/components/src/modal/index.tsx
var modal_exports = {};
__export(modal_exports, {
Modal: () => Modal,
default: () => modal_default
});
module.exports = __toCommonJS(modal_exports);
var import_clsx = __toESM(require("clsx"));
var import_element = require("@wordpress/element");
var import_compose = require("@wordpress/compose");
var import_i18n = require("@wordpress/i18n");
var import_icons = require("@wordpress/icons");
var import_dom = require("@wordpress/dom");
var ariaHelper = __toESM(require("./aria-helper"));
var import_button = __toESM(require("../button"));
var import_style_provider = __toESM(require("../style-provider"));
var import_with_ignore_ime_events = require("../utils/with-ignore-ime-events");
var import_spacer = require("../spacer");
var import_use_modal_exit_animation = require("./use-modal-exit-animation");
var import_jsx_runtime = require("react/jsx-runtime");
var ModalContext = (0, import_element.createContext)(/* @__PURE__ */ new Set());
ModalContext.displayName = "ModalContext";
var bodyOpenClasses = /* @__PURE__ */ new Map();
function UnforwardedModal(props, forwardedRef) {
const {
bodyOpenClassName = "modal-open",
role = "dialog",
title = null,
focusOnMount = true,
shouldCloseOnEsc = true,
shouldCloseOnClickOutside = true,
isDismissible = true,
/* Accessibility. */
aria = {
labelledby: void 0,
describedby: void 0
},
onRequestClose,
icon,
closeButtonLabel,
children,
style,
overlayClassName: overlayClassnameProp,
className,
contentLabel,
onKeyDown,
isFullScreen = false,
size,
headerActions = null,
__experimentalHideHeader = false
} = props;
const ref = (0, import_element.useRef)();
const instanceId = (0, import_compose.useInstanceId)(Modal);
const headingId = title ? `components-modal-header-${instanceId}` : aria.labelledby;
const focusOnMountRef = (0, import_compose.useFocusOnMount)(focusOnMount === "firstContentElement" ? "firstElement" : focusOnMount);
const constrainedTabbingRef = (0, import_compose.useConstrainedTabbing)();
const focusReturnRef = (0, import_compose.useFocusReturn)();
const contentRef = (0, import_element.useRef)(null);
const childrenContainerRef = (0, import_element.useRef)(null);
const [hasScrolledContent, setHasScrolledContent] = (0, import_element.useState)(false);
const [hasScrollableContent, setHasScrollableContent] = (0, import_element.useState)(false);
let sizeClass;
if (isFullScreen || size === "fill") {
sizeClass = "is-full-screen";
} else if (size) {
sizeClass = `has-size-${size}`;
}
const isContentScrollable = (0, import_element.useCallback)(() => {
if (!contentRef.current) {
return;
}
const closestScrollContainer = (0, import_dom.getScrollContainer)(contentRef.current);
if (contentRef.current === closestScrollContainer) {
setHasScrollableContent(true);
} else {
setHasScrollableContent(false);
}
}, [contentRef]);
(0, import_element.useEffect)(() => {
ariaHelper.modalize(ref.current);
return () => ariaHelper.unmodalize();
}, []);
const onRequestCloseRef = (0, import_element.useRef)();
(0, import_element.useEffect)(() => {
onRequestCloseRef.current = onRequestClose;
}, [onRequestClose]);
const dismissers = (0, import_element.useContext)(ModalContext);
const [nestedDismissers] = (0, import_element.useState)(() => /* @__PURE__ */ new Set());
(0, import_element.useEffect)(() => {
dismissers.add(onRequestCloseRef);
for (const dismisser of dismissers) {
if (dismisser !== onRequestCloseRef) {
dismisser.current?.();
}
}
return () => {
for (const dismisser of nestedDismissers) {
dismisser.current?.();
}
dismissers.delete(onRequestCloseRef);
};
}, [dismissers, nestedDismissers]);
(0, import_element.useEffect)(() => {
var _bodyOpenClasses$get;
const theClass = bodyOpenClassName;
const oneMore = 1 + ((_bodyOpenClasses$get = bodyOpenClasses.get(theClass)) !== null && _bodyOpenClasses$get !== void 0 ? _bodyOpenClasses$get : 0);
bodyOpenClasses.set(theClass, oneMore);
document.body.classList.add(bodyOpenClassName);
return () => {
const oneLess = bodyOpenClasses.get(theClass) - 1;
if (oneLess === 0) {
document.body.classList.remove(theClass);
bodyOpenClasses.delete(theClass);
} else {
bodyOpenClasses.set(theClass, oneLess);
}
};
}, [bodyOpenClassName]);
const {
closeModal,
frameRef,
frameStyle,
overlayClassname
} = (0, import_use_modal_exit_animation.useModalExitAnimation)();
(0, import_element.useLayoutEffect)(() => {
if (!window.ResizeObserver || !childrenContainerRef.current) {
return;
}
const resizeObserver = new ResizeObserver(isContentScrollable);
resizeObserver.observe(childrenContainerRef.current);
isContentScrollable();
return () => {
resizeObserver.disconnect();
};
}, [isContentScrollable, childrenContainerRef]);
function handleEscapeKeyDown(event) {
if (shouldCloseOnEsc && (event.code === "Escape" || event.key === "Escape") && !event.defaultPrevented) {
event.preventDefault();
closeModal().then(() => onRequestClose(event));
}
}
const onContentContainerScroll = (0, import_element.useCallback)((e) => {
var _e$currentTarget$scro;
const scrollY = (_e$currentTarget$scro = e?.currentTarget?.scrollTop) !== null && _e$currentTarget$scro !== void 0 ? _e$currentTarget$scro : -1;
if (!hasScrolledContent && scrollY > 0) {
setHasScrolledContent(true);
} else if (hasScrolledContent && scrollY <= 0) {
setHasScrolledContent(false);
}
}, [hasScrolledContent]);
let pressTarget = null;
const overlayPressHandlers = {
onPointerDown: (event) => {
if (event.target === event.currentTarget) {
pressTarget = event.target;
event.preventDefault();
}
},
// Closes the modal with two exceptions. 1. Opening the context menu on
// the overlay. 2. Pressing on the overlay then dragging the pointer
// over the modal and releasing. Due to the modal being a child of the
// overlay, such a gesture is a `click` on the overlay and cannot be
// excepted by a `click` handler. Thus the tactic of handling
// `pointerup` and comparing its target to that of the `pointerdown`.
onPointerUp: ({
target,
button
}) => {
const isSameTarget = target === pressTarget;
pressTarget = null;
if (button === 0 && isSameTarget) {
closeModal().then(() => onRequestClose());
}
}
};
const modal = (
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
ref: (0, import_compose.useMergeRefs)([ref, forwardedRef]),
className: (0, import_clsx.default)("components-modal__screen-overlay", overlayClassname, overlayClassnameProp),
onKeyDown: (0, import_with_ignore_ime_events.withIgnoreIMEEvents)(handleEscapeKeyDown),
...shouldCloseOnClickOutside ? overlayPressHandlers : {},
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_style_provider.default, {
document,
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
className: (0, import_clsx.default)("components-modal__frame", sizeClass, className),
style: {
...frameStyle,
...style
},
ref: (0, import_compose.useMergeRefs)([frameRef, constrainedTabbingRef, focusReturnRef, focusOnMount !== "firstContentElement" ? focusOnMountRef : null]),
role,
"aria-label": contentLabel,
"aria-labelledby": contentLabel ? void 0 : headingId,
"aria-describedby": aria.describedby,
tabIndex: -1,
onKeyDown,
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
className: (0, import_clsx.default)("components-modal__content", {
"hide-header": __experimentalHideHeader,
"is-scrollable": hasScrollableContent,
"has-scrolled-content": hasScrolledContent
}),
role: "document",
onScroll: onContentContainerScroll,
ref: contentRef,
"aria-label": hasScrollableContent ? (0, import_i18n.__)("Scrollable section") : void 0,
tabIndex: hasScrollableContent ? 0 : void 0,
children: [!__experimentalHideHeader && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
className: "components-modal__header",
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
className: "components-modal__header-heading-container",
children: [icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
className: "components-modal__icon-container",
"aria-hidden": true,
children: icon
}), title && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", {
id: headingId,
className: "components-modal__header-heading",
children: title
})]
}), headerActions, isDismissible && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, {
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_spacer.Spacer, {
marginBottom: 0,
marginLeft: 2
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_button.default, {
size: "compact",
onClick: (event) => closeModal().then(() => onRequestClose(event)),
icon: import_icons.close,
label: closeButtonLabel || (0, import_i18n.__)("Close")
})]
})]
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
ref: (0, import_compose.useMergeRefs)([childrenContainerRef, focusOnMount === "firstContentElement" ? focusOnMountRef : null]),
className: "components-modal__children-container",
children
})]
})
})
})
})
);
return (0, import_element.createPortal)(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ModalContext.Provider, {
value: nestedDismissers,
children: modal
}), document.body);
}
var Modal = (0, import_element.forwardRef)(UnforwardedModal);
var modal_default = Modal;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Modal
});
//# sourceMappingURL=index.js.map