@sparkpost/matchbox
Version:
A design system
133 lines (113 loc) • 2.9 kB
JavaScript
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { WindowEvent } from '../WindowEvent';
import styles from './Popover.module.scss';
class Popover extends Component {
static displayName = 'Popover';
static propTypes = {
/**
* A React component to will trigger the popover
* Click events are handled for you
*/
trigger: PropTypes.element,
/**
* If you want to control open state yourself, set this to true
*/
manualTrigger: PropTypes.bool,
/**
* Adds a padding to the Popover
*/
sectioned: PropTypes.bool,
/**
* Opens the popover
*/
open: PropTypes.bool,
left: PropTypes.bool,
right: PropTypes.bool,
top: PropTypes.bool,
bottom: PropTypes.bool,
onClose: PropTypes.func,
/**
* Popover Content
*/
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node
])
};
static defaultProps = {
right: true,
bottom: true
}
state = {
open: false
}
componentWillMount() {
this.setState({ open: this.props.open });
}
handleClose = (e) => {
const { onClose } = this.props;
this.setState({ open: false }, () => {
onClose && onClose();
});
}
handleClickOutside = (e) => {
if (this.state.open && this.wrapper && !this.wrapper.contains(e.target)) {
this.handleClose(e);
}
}
handleEsc = (e) => {
if (this.state.open && e.key === 'Escape') {
this.handleClose(e);
}
}
handleTrigger = () => {
if (!this.props.manualTrigger) {
this.setState({ open: true });
}
}
render() {
const {
children,
sectioned,
trigger,
className = '',
open,
manualTrigger,
top,
bottom,
left,
right,
wrapper: WrapperComponent = 'span',
...rest
} = this.props;
const shouldBeOpen = manualTrigger ? open : this.state.open;
const popoverClasses = classnames(
styles.Popover,
sectioned && styles.sectioned,
className
);
const wrapperClasses = classnames(
styles.Wrapper,
shouldBeOpen && styles.open,
top && styles.top,
left && styles.left
);
const triggerMarkup = <span onClick={this.handleTrigger}>{ trigger }</span>;
return (
<WrapperComponent className={wrapperClasses} ref={(wrapper) => this.wrapper = wrapper}>
<WindowEvent event='click' handler={this.handleClickOutside} />
<WindowEvent event='keydown' handler={this.handleEsc} />
{ triggerMarkup }
<div className={popoverClasses} {...rest}>
<span className={styles.Tip} />
<div className={styles.Content} >
{ children }
</div>
</div>
</WrapperComponent>
);
}
}
export default Popover;