@wix/design-system
Version:
@wix/design-system
173 lines • 9.5 kB
JavaScript
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