lucid-ui
Version:
A UI component library from Xandr.
119 lines • 4.44 kB
JavaScript
import _ from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
import Portal from '../Portal/Portal';
import { CSSTransition } from 'react-transition-group';
import { lucidClassNames, uniqueName } from '../../util/style-helpers';
const cx = lucidClassNames.bind('&-Overlay');
const { string, bool, func, node } = PropTypes;
export const overlayPropTypes = [
'children',
'className',
'isShown',
'isAnimated',
'isModal',
'portalId',
'onEscape',
'onBackgroundClick',
];
const nonPassThroughs = [...overlayPropTypes, 'initialState', 'callbackId'];
export const defaultProps = {
isAnimated: true,
isModal: true,
isShown: false,
onBackgroundClick: _.noop,
onEscape: _.noop,
};
class Overlay extends React.Component {
constructor() {
super(...arguments);
this.rootHTMLDivElement = React.createRef();
this.state = {
// This must be in state because getDefaultProps only runs once per
// component import which causes collisions
portalId: this.props.portalId || uniqueName('Overlay-Portal-'),
};
this.handleDocumentKeyDown = (event) => {
// If the user hits the "escape" key, then fire an `onEscape`
// TODO: use key helpers
if (event.keyCode === 27) {
this.props.onEscape({ event, props: this.props });
}
};
this.handleBackgroundClick = (event) => {
// Use the reference we previously stored from the `ref` to check what
// element was clicked on.
if (this.rootHTMLDivElement.current &&
event.target === this.rootHTMLDivElement.current) {
this.props.onBackgroundClick({ event, props: this.props });
}
};
}
componentDidMount() {
if (window && window.document) {
window.document.addEventListener('keydown', this.handleDocumentKeyDown);
}
}
componentWillUnmount() {
if (window && window.document) {
window.document.removeEventListener('keydown', this.handleDocumentKeyDown);
}
}
render() {
const { className, isShown, isModal, isAnimated, children, ...passThroughs } = this.props;
const { portalId } = this.state;
const overlayElement = (React.createElement("div", { ..._.omit(passThroughs, nonPassThroughs), className: cx(className, '&', {
'&-is-not-modal': !isModal,
'&-is-animated': isAnimated,
}), onClick: this.handleBackgroundClick, ref: this.rootHTMLDivElement }, children));
return (React.createElement(Portal, { portalId: portalId }, isAnimated ? (React.createElement(CSSTransition, { in: isShown, classNames: cx('&'), timeout: 300, unmountOnExit: true }, overlayElement)) : isShown ? (overlayElement) : null));
}
}
Overlay.displayName = 'Overlay';
Overlay.peek = {
description: `\`Overlay\` is used to block user interaction with the rest of the app until something has been completed.`,
categories: ['utility'],
madeFrom: ['Portal'],
};
Overlay.propTypes = {
/**
Appended to the component-specific class names set on the root element.
*/
className: string,
/**
Generally you should only have a single child element so the centering
works correctly.
*/
children: node,
/**
Controls visibility.
*/
isShown: bool,
/**
Enables animated transitions during expansion and collapse.
*/
isAnimated: bool,
/**
Determines if it shows with a gray background. If \`false\`, the
background will be rendered but will be invisible, except for the
contents, and it won't capture any of the user click events.
*/
isModal: bool,
/**
Set your own id for the \`Portal\` is that is opened up to contain the
contents. In practice you should never need to set this manually.
*/
portalId: string,
/**
Fired when the user hits escape. Signature: \`({ event, props }) => {}\`
*/
onEscape: func,
/**
Fired when the user clicks on the background, this may or may not be
visible depending on \`isModal\`. Signature: \`({ event, props }) => {}\`
*/
onBackgroundClick: func,
};
Overlay.defaultProps = defaultProps;
export default Overlay;
//# sourceMappingURL=Overlay.js.map