UNPKG

wix-style-react

Version:
204 lines • 11.2 kB
import React from 'react'; import PropTypes from 'prop-types'; import ReactModal from 'react-modal'; import defaultTo from 'lodash/defaultTo'; import { st, classes } from './Modal.st.css'; import { ariaDescribedBy, dataHooks, flexPositions, labels } from './constants'; import { ZIndex } from '../ZIndex'; import uniqueId from 'lodash/uniqueId'; import { WixStyleReactContext } from '../WixStyleReactProvider/context'; import WixStyleReactProvider from '../WixStyleReactProvider'; 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'; class Modal extends React.PureComponent { constructor(props) { super(props); 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.renderNavigationControls = () => { 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, skin: "inverted", size: "large", onClick: isPrevious ? onNavigationClickPrevious : onNavigationClickNext, dataHook: isPrevious ? dataHooks.navigationPrevButton : dataHooks.navigationNextButton }, isPrevious ? React.createElement(ArrowLeft, null) : React.createElement(ArrowRight, 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'); } render() { const { dataHook, horizontalPosition, verticalPosition, height, scrollableContent, borderRadius, zIndex, scrollable, isOpen, shouldCloseOnOverlayClick, shouldDisplayCloseButton, onRequestClose, onAfterClose, onAfterOpen, contentLabel, closeTimeoutMS, children, appElement, overlayPosition, parentSelector, screen, showNavigationPreviousButton, showNavigationNextButton, onNavigationClickPrevious, onNavigationClickNext, } = this.props; let { maxHeight } = this.props; const justifyContent = flexPositions[horizontalPosition]; const alignItems = 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: null, // null disables the property, use css instead // Overriding defaults - END display: 'flex', justifyContent, alignItems, overflowY: scrollable ? 'auto' : 'hidden', }, 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("div", { "data-hook": dataHook, onKeyDown: e => { const arrows = { left: 'ArrowLeft', right: 'ArrowRight', }; if (e && e.key === arrows.left) { showNavigationPreviousButton && onNavigationClickPrevious && onNavigationClickPrevious(); } if (e && e.key === arrows.right) { showNavigationNextButton && onNavigationClickNext && onNavigationClickNext(); } } }, React.createElement(WixStyleReactContext.Consumer, null, ({ newColorsBranding }) => (React.createElement(ReactModal, { portalClassName: st(classes.root, { scrollable }, `portal portal-${dataHook}`), isOpen: isOpen, shouldCloseOnOverlayClick: shouldCloseOnOverlayClick, onRequestClose: onRequestClose, onAfterOpen: onAfterOpen, onAfterClose: onAfterClose, style: modalStyles, className: classes.modal, closeTimeoutMS: closeTimeoutMS, parentSelector: parentSelector, ariaHideApp: false, contentLabel: contentLabel, aria: { labelledby: ariaLabels.labelledBy, describedby: ariaLabels.describedBy, } }, React.createElement(WixStyleReactProvider, { features: { newColorsBranding } }, 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(), children))))))); } } Modal.propTypes = { /** Applied as data-hook HTML attribute that can be used to create driver in testing */ dataHook: PropTypes.string, /** Controls if modal is open or closed */ isOpen: PropTypes.bool.isRequired, /** Border radius of modal */ borderRadius: PropTypes.number, /** a11y: The value of contentLabel is set as an aria-label on the modal element. This helps assistive technology, like screen readers, to add a label to an element that would otherwise be anonymous */ contentLabel: PropTypes.string, /** Renders modal content */ children: PropTypes.any, /** Controls z-index of the modal overlay */ zIndex: PropTypes.number, /** Enables to close modal when mouse clicked on overlay area */ shouldCloseOnOverlayClick: PropTypes.bool, /** Displays a close button on the top right corner of the overlay */ shouldDisplayCloseButton: PropTypes.bool, /** Callback that will be executed when the modal is requested to be closed, prior to actually closing */ onRequestClose: PropTypes.func, /** Callback that will be executed after the modal has been opened */ onAfterOpen: PropTypes.func, /** Callback that will be executed after the modal has been closed */ onAfterClose: PropTypes.func, /** Horizontal position of the modal */ horizontalPosition: PropTypes.oneOf(['start', 'center', 'end']), /** Vertical position of the modal */ verticalPosition: PropTypes.oneOf(['start', 'center', 'end']), /** Number indicating the milliseconds to wait before closing the modal */ closeTimeoutMS: PropTypes.number, /** Specifies if modal portal supports scroll */ scrollable: PropTypes.bool, /** Specifies if modal content should become scrollable when modal size will fit the window */ scrollableContent: PropTypes.bool, /** Sets the maximum height for a scrollable content */ maxHeight: PropTypes.string, /** Sets the height for modal's content container */ height: PropTypes.string, /** css position of the modal overlay */ overlayPosition: PropTypes.oneOf([ 'static', 'relative', 'absolute', 'fixed', 'sticky', ]), /** A function that returns a DOM element on which the modal should be appended to */ parentSelector: PropTypes.func, /** Selector specifying where to apply the aria-hidden attribute */ appElement: PropTypes.string, /** Specifies minimum spacing between full viewport and modal content */ screen: PropTypes.oneOf(['full', 'desktop', 'mobile']), /** Enable navigation previous buttons to the side of the content and listen on clicks by using onNavigationClickPrevious */ showNavigationPreviousButton: PropTypes.bool, /** Enable navigation next button to the side of the content and listen on clicks by using onNavigationClickNext */ showNavigationNextButton: PropTypes.bool, /** Callback that will be executed when the navigation control previous is clicked */ onNavigationClickPrevious: PropTypes.func, /** Callback that will be executed when the navigation control next is clicked */ onNavigationClickNext: PropTypes.func, /** Label used for navigation control previous button tooltip */ navigationPreviousLabel: PropTypes.string, /** Label used for navigation control next button tooltip */ navigationNextLabel: PropTypes.string, }; 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', }; export default Modal; //# sourceMappingURL=Modal.js.map