UNPKG

react-conventions

Version:

An open source set of React components that implement Ambassador's Design and UX patterns.

143 lines (127 loc) 4.16 kB
import React from 'react' import classNames from 'classnames/bind' import style from './style.scss' import Overlay from './Overlay' import RenderToLayer from '../internal/RenderToLayer' import Icon from '../Icon' /** * The Modal component. */ class Modal extends React.Component { constructor(props) { super(props) } static defaultProps = { open: false, closeOnAction: false } static propTypes = { /** * Action buttons to display below the Modal content (`children`). * This property accepts either a React element, or an array of React elements. */ actions: React.PropTypes.node, /** * Controls whether the Modal is opened or not. */ open: React.PropTypes.bool.isRequired, /** * When set to true it will force the user to use one of the actions in the `Modal`. * Clicking outside the `Modal` will not trigger the `onRequestClose` in that case. */ closeOnAction: React.PropTypes.bool, /** * Fired when the `Modal` is requested to be closed by a click outside the `Modal` or on the buttons. * * @param {bool} buttonClicked Determines whether a button click triggered this request. */ onRequestClose: React.PropTypes.func, /** * The title to display on the `Modal`. Could be number, string, element or an array containing these types. */ title: React.PropTypes.node, /** * Optional styles to add to the modal. */ optClass: React.PropTypes.string, /** * The size of the modal. The default is 'md' (medium). */ size: React.PropTypes.oneOf(['sm', 'md', 'lg']) } handleKeyUp = (event) => { // When Esc is pressed if (event.keyCode === 27) { this.requestClose(false) } } handleClick = (event) => { event.persist() if (typeof event.target.className === 'string' && event.target.className.indexOf('modal-scroll-container') !== -1) { this.handleCloseClick() } } handleCloseClick = () => { this.requestClose(false) } requestClose = (buttonClicked) => { if (!buttonClicked && this.props.closeOnAction) { return } if (this.props.onRequestClose) { this.props.onRequestClose(!!buttonClicked) } } setKeyupListener = () => { if (this.props.open) { window.addEventListener('keyup', this.handleKeyUp) } else { window.removeEventListener('keyup', this.handleKeyUp) } } renderModal = () => { const cx = classNames.bind(style) const modalOpenClass = this.props.open ? style['modal-open'] : '' const modalSizeClass = this.props.size ? style['modal-' + this.props.size] : '' const modalClass = cx(style['modal-component'], this.props.optClass, modalOpenClass) const modalContentClass = cx(style['modal-content'], modalSizeClass) const actionsContainer = React.Children.count(this.props.actions) > 0 && ( <div className={style['modal-actions']}> {React.Children.toArray(this.props.actions)} </div> ) this.setKeyupListener() return ( <div className={modalClass}> <div className={style['modal-scroll-container']} onClick={this.handleClick}> <Overlay show={this.props.open} onClick={this.handleCloseClick} /> <div className={modalContentClass}> <div className={style['modal-header']}> {!this.props.closeOnAction ? <div className={style['modal-close']}> <Icon name='icon-delete-1' width='12' height='12' onClick={this.handleCloseClick} /> </div> : null} {this.props.title ? <h1>{this.props.title}</h1> : null} </div> <div className={style['modal-body']}> {this.props.children} </div> <div className={style['modal-footer']}> {actionsContainer} </div> </div> </div> </div> ) } render() { return ( // Render the modal inside a div at the bottom of the document body <RenderToLayer render={this.renderModal} open={true} /> ) } } export default Modal