UNPKG

@wix/design-system

Version:

@wix/design-system

173 lines 9.5 kB
import React, { PureComponent } from 'react'; import ReactModal from 'react-modal'; import defaultTo from 'lodash/defaultTo'; import { st, classes } from './Modal.st.css.js'; import { ariaDescribedBy, dataHooks, flexPositions, labels } from './constants'; import { ZIndex } from '../common/ZIndex'; import uniqueId from 'lodash/uniqueId'; import WixDesignSystemProvider from '../WixDesignSystemProvider'; import { WixDesignSystemContext } from '../WixDesignSystemProvider/context'; import { ariaLabels } from '../common/accessibility/ariaLabels'; import CloseButton from '../CloseButton'; import IconButton from '../IconButton'; import { ArrowLeft, ArrowRight } from '@wix/wix-ui-icons-common'; import Tooltip from '../Tooltip'; import { IconThemeContext } from '../WixDesignSystemIconThemeProvider/IconThemeContext'; const defaultParentSelector = () => (typeof document === 'undefined' ? null : document.body); class Modal extends PureComponent { constructor(props) { super(props); this.triggerElementRef = null; this.handleOverlayClick = (event) => { const { shouldCloseOnOverlayClick, onRequestClose } = this.props; if (shouldCloseOnOverlayClick && event.target.id === this.CHILDREN_WRAPPER_DIV_ID && onRequestClose) { onRequestClose(); } }; this.renderCloseButton = () => { return (React.createElement(CloseButton, { dataHook: dataHooks.modalCloseButton, className: classes.closeButton, onClick: this.props.onRequestClose, size: "large", skin: "light", "aria-label": labels.close })); }; this.handleOnKeyDown = (e) => { const { showNavigationPreviousButton, onNavigationClickPrevious, onNavigationClickNext, showNavigationNextButton, } = this.props; const arrows = { left: 'ArrowLeft', right: 'ArrowRight', }; if (e && e.key === arrows.left) { showNavigationPreviousButton && onNavigationClickPrevious && onNavigationClickPrevious(); } if (e && e.key === arrows.right) { showNavigationNextButton && onNavigationClickNext && onNavigationClickNext(); } }; this.renderNavigationControls = (ArrowLeftIcon, ArrowRightIcon) => { const DIRECTIONS = { prev: 'prev', next: 'next' }; const TOOLTIP_GAP = 9; const { showNavigationPreviousButton, showNavigationNextButton, onNavigationClickPrevious, onNavigationClickNext, navigationPreviousLabel, navigationNextLabel, } = this.props; const buildButton = (dir, className) => { const isPrevious = dir === DIRECTIONS.prev; return (React.createElement(IconButton, { className: className, priority: "secondary", size: "large", onClick: isPrevious ? onNavigationClickPrevious : onNavigationClickNext, dataHook: isPrevious ? dataHooks.navigationPrevButton : dataHooks.navigationNextButton }, isPrevious ? React.createElement(ArrowLeftIcon, null) : React.createElement(ArrowRightIcon, null))); }; return (React.createElement(React.Fragment, null, showNavigationPreviousButton && (navigationPreviousLabel ? (React.createElement(Tooltip, { className: classes.navPrev, content: navigationPreviousLabel, moveBy: { y: TOOLTIP_GAP }, "aria-describedby": ariaDescribedBy.navigationPrev, dataHook: dataHooks.navigationPrevTooltip }, buildButton(DIRECTIONS.prev))) : (buildButton(DIRECTIONS.prev, classes.navPrev))), showNavigationNextButton && (navigationNextLabel ? (React.createElement(Tooltip, { className: classes.navNext, content: navigationNextLabel, moveBy: { y: TOOLTIP_GAP }, "aria-describedby": ariaDescribedBy.navigationNext, dataHook: dataHooks.navigationNextTooltip }, buildButton(DIRECTIONS.next))) : (buildButton(DIRECTIONS.next, classes.navNext))))); }; this.CHILDREN_WRAPPER_DIV_ID = uniqueId('wsr-modal'); } componentDidUpdate(prevProps) { if (!prevProps.isOpen && this.props.isOpen) { this.triggerElementRef = document.activeElement; } if (prevProps.isOpen && !this.props.isOpen) { if (this.triggerElementRef && typeof this.triggerElementRef.focus === 'function' && this.triggerElementRef.isConnected) { this.triggerElementRef.focus(); } this.triggerElementRef = null; } } render() { const { dataHook, horizontalPosition, verticalPosition, height, scrollableContent, borderRadius, zIndex, scrollable, isOpen, shouldCloseOnOverlayClick, shouldDisplayCloseButton, onRequestClose, onAfterClose, contentLabel, closeTimeoutMS, children, appElement, overlayPosition, parentSelector, screen, showNavigationPreviousButton, showNavigationNextButton, transition, onAfterOpen, } = this.props; let { maxHeight } = this.props; const { contextClassName } = this.context; const justifyContent = // @ts-expect-error Typescript doesn't know about Modal.defaultProps flexPositions[horizontalPosition]; const alignItems = // @ts-expect-error Typescript doesn't know about Modal.defaultProps flexPositions[verticalPosition]; maxHeight = scrollableContent && maxHeight === 'auto' ? '100vh' : maxHeight; const modalStyles = { overlay: { // Overriding defaults position: overlayPosition, top: 0, left: 0, right: 0, bottom: 0, zIndex: defaultTo(zIndex, ZIndex.modal), backgroundColor: undefined, // undefined disables the property, use css instead // Overriding defaults - END display: 'flex', justifyContent, alignItems, overflowY: scrollable && transition !== 'moveVertical' ? 'auto' : 'clip', overflowX: transition === 'moveHorizontal' ? 'clip' : undefined, }, content: { // Overriding defaults border: 'none', overflowY: scrollableContent ? 'auto' : 'initial', overflowX: scrollableContent ? 'hidden' : 'initial', height, maxHeight, width: '100%', // stylelint-disable-line WebkitOverflowScrolling: 'touch', outline: 'none', borderRadius, padding: '0px', // Overriding defaults - END backgroundColor: 'transparent', marginBottom: '0px', position: 'relative', }, }; if (appElement) { ReactModal.setAppElement(appElement); } else { ReactModal.setAppElement('body'); } return (React.createElement(IconThemeContext.Consumer, null, ({ icons = {} }) => { const ArrowLeftIcon = icons.Modal?.ArrowLeft || ArrowLeft; const ArrowRightIcon = icons.Modal?.ArrowRight || ArrowRight; return (React.createElement("div", { "data-hook": dataHook, onKeyDown: this.handleOnKeyDown }, React.createElement(ReactModal, { portalClassName: st(classes.root, { scrollable, transition }, `portal portal-${dataHook}`, contextClassName), isOpen: isOpen, shouldCloseOnOverlayClick: shouldCloseOnOverlayClick, onRequestClose: onRequestClose, onAfterOpen: onAfterOpen, onAfterClose: onAfterClose, style: modalStyles, className: classes.modal, closeTimeoutMS: closeTimeoutMS, parentSelector: parentSelector ?? defaultParentSelector, ariaHideApp: false, contentLabel: contentLabel, aria: { labelledby: ariaLabels.labelledBy, describedby: ariaLabels.describedBy, } }, React.createElement(WixDesignSystemProvider, { className: classes.designSystemContextProvider, as: "div" }, isOpen && shouldDisplayCloseButton && this.renderCloseButton(), React.createElement("div", { "data-scrollable": scrollable || null, id: this.CHILDREN_WRAPPER_DIV_ID, className: st(classes.childrenContainer, { screen, }), onClick: this.handleOverlayClick }, (showNavigationPreviousButton || showNavigationNextButton) && this.renderNavigationControls(ArrowLeftIcon, ArrowRightIcon), children))))); })); } } Modal.displayName = 'Modal'; Modal.contextType = WixDesignSystemContext; Modal.defaultProps = { borderRadius: 0, shouldCloseOnOverlayClick: false, shouldDisplayCloseButton: false, horizontalPosition: 'center', verticalPosition: 'center', closeTimeoutMS: 500, scrollable: true, scrollableContent: false, height: '100%', maxHeight: 'auto', overlayPosition: 'fixed', screen: 'desktop', transition: 'scale', }; export default Modal; //# sourceMappingURL=Modal.js.map