UNPKG

@wordpress/components

Version:
284 lines (282 loc) 12.1 kB
"use strict"; 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