Repository URL to install this package:
|
Version:
8.1.0-rc.5 ▾
|
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import Modal from '../Modal';
import Button from '../../../controls/Button/Button';
import ButtonLabel from '../../../controls/Button/ButtonLabel';
import LoadingButton from '../../../controls/Button/LoadingButton';
import Icon from '../../../visuals/Icon/Icon';
import Close from '../../../visuals/Icon/svg/ic_close.svg';
const buttonShape = {
onClick: PropTypes.func,
label: PropTypes.string,
href: PropTypes.string,
disabled: PropTypes.bool,
loading: PropTypes.bool,
autoFocus: PropTypes.bool,
};
class StandardModal extends Component {
static propTypes = {
/** Modal contents displayed over underlay, no styling/wrapper provided */
content: PropTypes.any.isRequired,
/** A11y-enabled modal title */
title: PropTypes.string.isRequired,
/** Image to be used as part of the Modal Header, when this image is added, the close icon is not shown */
Image: PropTypes.elementType,
/** Control whether to show the modal: this allows internal entering/exiting logic for transitions */
show: PropTypes.bool.isRequired,
/** If provided, it is invoked for escape key or underlay click interactions. If omitted, the modal dialog does not respond to these interactions: you have to provide a way of closing it from the modal dialog content (an interaction that will toggle the `show` prop) . */
onExit: PropTypes.func,
/** If providedm it is invoked as soon as the modal is an entered/opened state */
onEnter: PropTypes.func,
/** The primary button of the modal */
primaryAction: PropTypes.shape(buttonShape),
/** The secondary button of the modal */
secondaryAction: PropTypes.shape(buttonShape),
/** The tertiary button of the modal. Will take precedence if both this and the hint are provided */
tertiaryAction: PropTypes.shape(buttonShape),
/** A hint that will only be rendered if no tertiary button is provided */
hint: PropTypes.string,
/** Allows the StandardModal to grow in width beyond the 640px limit */
wide: PropTypes.bool,
};
static defaultProps = {
Image: undefined,
onExit: undefined,
onEnter: undefined,
primaryAction: undefined,
secondaryAction: undefined,
tertiaryAction: undefined,
hint: undefined,
wide: false,
};
constructor(props) {
super(props);
this.state = {
verticalAlign: window.innerWidth < 640 ? 'bottom' : 'middle',
};
}
componentDidMount() {
window.addEventListener('resize', this.getScreenInnerWidth);
}
componentWillUnmount() {
window.addEventListener('resize', this.getScreenInnerWidth);
}
onKeyPress = event => {
const { onExit } = this.props;
const enterPressed = event.key === 'Enter';
if (enterPressed) onExit();
};
getScreenInnerWidth = () => {
const isSmallScreen = window.innerWidth < 640;
this.setState({
verticalAlign: isSmallScreen ? 'bottom' : 'middle',
verticalBottomDialogOffset: isSmallScreen ? '5vw' : null,
});
};
renderFooter = () => {
const { primaryAction, secondaryAction, tertiaryAction, hint } = this.props;
const shouldRenderFooter = primaryAction || secondaryAction || tertiaryAction || hint;
if (shouldRenderFooter) {
return (
<div className="StandardModal-footer">
{tertiaryAction && (
<Button
autoFocus={!!tertiaryAction.autoFocus}
onClick={tertiaryAction.onClick}
href={tertiaryAction.href}
variant="linkBlue"
disabled={tertiaryAction.disabled}
inputButtonClass={tertiaryAction.loading ? 'loading' : null}
>
<ButtonLabel>{tertiaryAction.label}</ButtonLabel>
<LoadingButton />
</Button>
)}
{!tertiaryAction && hint && <div className="StandardModal-hint tiny--soft">{hint}</div>}
<div className="StandardModal-buttonWrapper">
{secondaryAction && (
<Button
autoFocus={!!secondaryAction.autoFocus}
onClick={secondaryAction.onClick}
href={secondaryAction.href}
variant="whiteWithBorder"
disabled={secondaryAction.disabled}
inputButtonClass={secondaryAction.loading ? 'loading' : null}
>
<ButtonLabel>{secondaryAction.label}</ButtonLabel>
<LoadingButton />
</Button>
)}
{primaryAction && (
<Button
className="StandardModal-primaryButton"
autoFocus={!!primaryAction.autoFocus}
onClick={primaryAction.onClick}
href={primaryAction.href}
variant="blue"
disabled={primaryAction.disabled}
inputButtonClass={primaryAction.loading ? 'loading' : null}
>
<ButtonLabel>{primaryAction.label}</ButtonLabel>
<LoadingButton />
</Button>
)}
</div>
</div>
);
}
return null;
};
render() {
const { content, title, Image, onExit, wide } = this.props;
const { verticalAlign, verticalBottomDialogOffset } = this.state;
const classNames = classnames('StandardModal-container', {
'StandardModal-container--wide': wide,
});
return (
<Modal
{...this.props}
variant="ink"
verticalAlign={verticalAlign}
verticalBottomDialogOffset={verticalBottomDialogOffset}
>
<div className={classNames}>
{Image && <Image className="StandardModal-image" />}
<div className="StandardModal-header">
<h2 className="StandardModal-title">{title}</h2>
{!Image && (
<Icon
className="StandardModal-close"
icon={Close}
tabIndex="0"
onKeyPress={this.onKeyPress}
onClick={onExit}
/>
)}
</div>
<div className="StandardModal-content body">{content}</div>
{this.renderFooter()}
</div>
</Modal>
);
}
}
export default StandardModal;