wix-style-react
Version:
wix-style-react
204 lines • 11.2 kB
JavaScript
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