zepio/app/components/modal.js

114 lines
2.4 KiB
JavaScript
Raw Normal View History

2018-12-19 05:50:08 -08:00
// @flow
2019-02-04 20:41:45 -08:00
2018-12-19 05:50:08 -08:00
import React, { PureComponent, Fragment, type Element } from 'react';
import { createPortal } from 'react-dom';
import styled from 'styled-components';
const ModalWrapper = styled.div`
width: 100vw;
height: 100vh;
position: fixed;
display: flex;
align-items: center;
justify-content: center;
top: 0;
left: 0;
z-index: 10;
background-color: rgba(0, 0, 0, 0.5);
2018-12-19 05:50:08 -08:00
`;
const ChildrenWrapper = styled.div`
z-index: 90;
`;
type Props = {
renderTrigger: (() => void) => Element<*>,
children: (() => void) => Element<*>,
closeOnBackdropClick?: boolean,
closeOnEsc?: boolean,
2018-12-19 05:50:08 -08:00
};
type State = {
isVisible: boolean,
};
const modalRoot = document.getElementById('modal-root');
export class ModalComponent extends PureComponent<Props, State> {
element = document.createElement('div');
static defaultProps = {
closeOnBackdropClick: true,
closeOnEsc: true,
};
2018-12-19 05:50:08 -08:00
state = {
isVisible: false,
};
componentDidMount() {
const { closeOnEsc } = this.props;
if (closeOnEsc) {
window.addEventListener('keydown', this.handleEscPress);
}
2018-12-19 05:50:08 -08:00
}
componentWillUnmount() {
const { closeOnEsc } = this.props;
if (closeOnEsc) {
window.removeEventListener('keydown', this.handleEscPress);
}
2018-12-19 05:50:08 -08:00
}
handleEscPress = (event: Object) => {
const { isVisible } = this.state;
if (event.key === 'Escape' && isVisible) {
this.close();
}
};
2019-02-04 20:41:45 -08:00
open = () => this.setState(
() => ({ isVisible: true }),
() => {
if (modalRoot) modalRoot.appendChild(this.element);
},
);
2018-12-19 05:50:08 -08:00
2019-02-04 20:41:45 -08:00
close = () => this.setState(
() => ({ isVisible: false }),
() => {
if (modalRoot) modalRoot.removeChild(this.element);
},
);
2018-12-19 05:50:08 -08:00
render() {
const { renderTrigger, children, closeOnBackdropClick } = this.props;
2018-12-19 05:50:08 -08:00
const { isVisible } = this.state;
const toggleVisibility = isVisible ? this.close : this.open;
2018-12-19 05:50:08 -08:00
return (
<Fragment>
{renderTrigger(toggleVisibility)}
2019-02-04 20:41:45 -08:00
{!isVisible ? null : createPortal(
<ModalWrapper
id='modal-portal-wrapper'
data-testid='Modal'
onClick={(event) => {
if (
closeOnBackdropClick
&& event.target.id === 'modal-portal-wrapper'
) this.close();
}}
>
<ChildrenWrapper>{children(toggleVisibility)}</ChildrenWrapper>
</ModalWrapper>,
this.element,
)}
2018-12-19 05:50:08 -08:00
</Fragment>
);
}
}