import * as React from 'react'; import {Portal} from './Portal'; import {withOverlayStackSynchronyzer} from '../overlay_stack/synchronizer'; import {joinClassNames} from '../../../../utils/joinClassNames'; import {filterProps} from '../../../../utils/filterProps'; import { DefaultProps, IProps, FullProps, FullPropsWithDefault } from './Overlay.types'; import * as theme from './overlay.m.scss'; class UnwrappedBasicOverlay extends React.PureComponent { static defaultProps: DefaultProps = { shouldCloseOnClickOutside: true, shouldCloseOnEsc: true, hasBackdrop: false }; static excludingProps: Array = [ 'isLast', 'isLastBackdrop', 'isContainsInUpperOverlays', 'isOpen', 'hasBackdrop', 'shouldCloseOnClickOutside', 'shouldCloseOnEsc', 'styles', 'onClose' ]; private overlayElement = React.createRef(); private handleOutsideClick: EventListener = (e) => { const {isOpen, hasBackdrop, onClose, shouldCloseOnClickOutside, isContainsInUpperOverlays} = this .props as FullPropsWithDefault; const eventTarget = e.target as Element; if ( shouldCloseOnClickOutside && isOpen && !hasBackdrop && onClose && this.overlayElement.current && !this.overlayElement.current.contains(eventTarget) && !isContainsInUpperOverlays(eventTarget) ) { onClose(e); } }; private handleOverlayClick: React.MouseEventHandler = (e) => { const {isOpen, onClick, hasBackdrop, onClose, shouldCloseOnClickOutside} = this.props as FullPropsWithDefault; const eventTarget = e.target as Element; if ( shouldCloseOnClickOutside && onClose && isOpen && hasBackdrop && eventTarget === this.overlayElement.current ) { onClose(e); } if (onClick) { onClick(e); } }; private handleKeyUp: EventListener = (e: KeyboardEvent) => { const {isOpen, onClose, shouldCloseOnEsc, isLast} = this.props as FullPropsWithDefault; if ( isLast && shouldCloseOnEsc && onClose && isOpen && e.key === 'Escape' ) { onClose(e); } }; override componentDidMount () { document.body.addEventListener('click', this.handleOutsideClick); document.body.addEventListener('keyup', this.handleKeyUp); } override componentWillUnmount () { document.body.removeEventListener('click', this.handleOutsideClick); document.body.removeEventListener('keyup', this.handleKeyUp); } private renderBackdrop () { const {hasBackdrop, styles} = this.props; const style = styles && styles.backdrop; if (hasBackdrop) { return
; } return null; } override render () { if (!this.props.isOpen) { return null; } const {styles, hasBackdrop, isLastBackdrop, isLast} = this.props as FullPropsWithDefault; const style = styles && styles.overlay; const className = joinClassNames( theme.overlay, [theme.overlayHasBackdrop, hasBackdrop], [theme.overlayIsLast, isLast], [theme.overlayIsLastBackdrop, isLastBackdrop], ); const {children, ...htmlProps} = { ...filterProps>( this.props, UnwrappedBasicOverlay.excludingProps, ), ref: this.overlayElement, style, className, onClick: this.handleOverlayClick }; return (
{this.renderBackdrop()} {children}
); } } export const Overlay = withOverlayStackSynchronyzer(UnwrappedBasicOverlay);