UNPKG

@shopify/polaris

Version:

Shopify’s admin product component library

154 lines (151 loc) 5.12 kB
import React, { useState, useId, useRef, useCallback } from 'react'; import { TransitionGroup } from 'react-transition-group'; import { focusFirstFocusableNode } from '../../utilities/focus.js'; import { WithinContentContext } from '../../utilities/within-content-context.js'; import { wrapWithComponent } from '../../utilities/components.js'; import styles from './Modal.css.js'; import { Section } from './components/Section/Section.js'; import { Dialog } from './components/Dialog/Dialog.js'; import { Header } from './components/Header/Header.js'; import { Backdrop } from '../Backdrop/Backdrop.js'; import { Footer } from './components/Footer/Footer.js'; import { useI18n } from '../../utilities/i18n/hooks.js'; import { Box } from '../Box/Box.js'; import { Scrollable } from '../Scrollable/Scrollable.js'; import { Portal } from '../Portal/Portal.js'; import { InlineStack } from '../InlineStack/InlineStack.js'; import { Spinner } from '../Spinner/Spinner.js'; const IFRAME_LOADING_HEIGHT = 200; const DEFAULT_IFRAME_CONTENT_HEIGHT = 400; const Modal = function Modal({ children, title, titleHidden = false, src, iFrameName, open, instant, sectioned, loading, size, limitHeight, footer, primaryAction, secondaryActions, onScrolledToBottom, activator, activatorWrapper = 'div', onClose, onIFrameLoad, onTransitionEnd, noScroll }) { const [iframeHeight, setIframeHeight] = useState(IFRAME_LOADING_HEIGHT); const [closing, setClosing] = useState(false); const headerId = useId(); const activatorRef = useRef(null); const i18n = useI18n(); const iframeTitle = i18n.translate('Polaris.Modal.iFrameTitle'); let dialog; let backdrop; const handleEntered = useCallback(() => { if (onTransitionEnd) { onTransitionEnd(); } }, [onTransitionEnd]); const handleExited = useCallback(() => { setIframeHeight(IFRAME_LOADING_HEIGHT); const activatorElement = activator && isRef(activator) ? activator && activator.current : activatorRef.current; if (activatorElement) { requestAnimationFrame(() => focusFirstFocusableNode(activatorElement)); } }, [activator]); const handleIFrameLoad = useCallback(evt => { const iframe = evt.target; if (iframe && iframe.contentWindow) { try { setIframeHeight(iframe.contentWindow.document.body.scrollHeight); } catch (_error) { setIframeHeight(DEFAULT_IFRAME_CONTENT_HEIGHT); } } if (onIFrameLoad != null) { onIFrameLoad(evt); } }, [onIFrameLoad]); if (open) { const footerMarkup = !footer && !primaryAction && !secondaryActions ? null : /*#__PURE__*/React.createElement(Footer, { primaryAction: primaryAction, secondaryActions: secondaryActions }, footer); const content = sectioned ? wrapWithComponent(children, Section, { titleHidden }) : children; const body = loading ? /*#__PURE__*/React.createElement(Box, { padding: "400" }, /*#__PURE__*/React.createElement(InlineStack, { gap: "400", align: "center", blockAlign: "center" }, /*#__PURE__*/React.createElement(Spinner, null))) : content; const scrollContainerMarkup = noScroll ? /*#__PURE__*/React.createElement("div", { className: styles.NoScrollBody }, /*#__PURE__*/React.createElement(Box, { width: "100%", overflowX: "hidden", overflowY: "hidden" }, body)) : /*#__PURE__*/React.createElement(Scrollable, { shadow: true, className: styles.Body, onScrolledToBottom: onScrolledToBottom }, body); const bodyMarkup = src ? /*#__PURE__*/React.createElement("iframe", { name: iFrameName, title: iframeTitle, src: src, className: styles.IFrame, onLoad: handleIFrameLoad, style: { height: `${iframeHeight}px` } }) : scrollContainerMarkup; dialog = /*#__PURE__*/React.createElement(Dialog, { instant: instant, labelledBy: headerId, onClose: onClose, onEntered: handleEntered, onExited: handleExited, size: size, limitHeight: limitHeight, setClosing: setClosing }, /*#__PURE__*/React.createElement(Header, { titleHidden: titleHidden, id: headerId, closing: closing, onClose: onClose }, title), bodyMarkup, footerMarkup); backdrop = /*#__PURE__*/React.createElement(Backdrop, { setClosing: setClosing, onClick: onClose }); } const animated = !instant; const activatorMarkup = activator && !isRef(activator) ? /*#__PURE__*/React.createElement(Box, { ref: activatorRef, as: activatorWrapper }, activator) : null; return /*#__PURE__*/React.createElement(WithinContentContext.Provider, { value: true }, activatorMarkup, /*#__PURE__*/React.createElement(Portal, { idPrefix: "modal" }, /*#__PURE__*/React.createElement(TransitionGroup, { appear: animated, enter: animated, exit: animated }, dialog), backdrop)); }; function isRef(ref) { return Object.prototype.hasOwnProperty.call(ref, 'current'); } Modal.Section = Section; export { Modal };