UNPKG

lucid-ui

Version:

A UI component library from Xandr.

119 lines 4.44 kB
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