UNPKG

wix-style-react

Version:
138 lines (121 loc) 3.7 kB
import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import Popover from '../../Popover'; import { Mode } from './constants'; const controlledErrorMsg = method => `ClosablePopover.${method}() can not be called when component is Controlled. (opened prop should be undefined)`; /** * Closable Popover * Either a normal Controlled Popover, or a Popover that is initially opened and can be the closed by * calling a closeAction. */ class ClosablePopover extends PureComponent { state = { open: this.props.initiallyOpened, mode: this.props.initiallyOpened ? Mode.ClickToClose : Mode.Hover, }; _isControlled() { return typeof this.props.opened === 'boolean'; } open = () => this._doOpen(Mode.ClickToClose); _doOpen = nextMode => { if (this._isControlled()) { throw new Error(controlledErrorMsg('open')); } !this.state.open && this.setState( { open: true, mode: nextMode, }, () => { this.props.onOpen && this.props.onOpen(); }, ); }; close = () => { if (this._isControlled()) { throw new Error(controlledErrorMsg('close')); } this.state.open && this.setState( { open: false, mode: Mode.Hover, }, () => { this.props.onClose && this.props.onClose(); }, ); }; _handleMouseEnter = () => { if (this.state.mode === Mode.Hover) { this._doOpen(Mode.Hover); } }; _handleMouseLeave = () => { if (this.state.mode === Mode.Hover && this.props.closeOnMouseLeave) { this.close(); } }; render() { const { content, appearance, target, children, onClose, onOpen, initiallyOpened, closeOnMouseLeave, hideDelay, ...rest } = this.props; const open = this._isControlled() ? this.props.opened : this.state.open; const popoverProps = { ...rest, animate: true, theme: appearance, shown: open, onMouseEnter: this._handleMouseEnter, onMouseLeave: this._handleMouseLeave, }; return ( <Popover {...popoverProps}> <Popover.Element>{target}</Popover.Element> <Popover.Content>{content(this.actions)}</Popover.Content> </Popover> ); } actions = { close: this.close, }; } ClosablePopover.defaultProps = { hideDelay: 0, initiallyOpened: true, closeOnMouseLeave: true, }; ClosablePopover.propTypes = { /** Controls whether the popover's content is shown or not. * When undefined, then the component is Uncontrolled, * It is initially open, and it can be closed by close-action */ opened: PropTypes.bool, /** Controls whether the popover's content is initially opened (in Uncontrolled mode only) */ initiallyOpened: PropTypes.bool, /** The popover's target element*/ target: PropTypes.node.isRequired, /** Callback to call when the popover content is requested to be opened (Uncontrolled mode only) */ onOpen: PropTypes.func, /** callback to call when the popover content is requested to be closed (Uncontrolled mode only). NOTE: this callback is called when the close timeout (if exists) starts */ onClose: PropTypes.func, /** Disable close on mouseLeave */ closeOnMouseLeave: PropTypes.bool, /** The popover's content, given as a function that receives control-actions and renders the contet. * In Uncontrolled mode, this function is still called only once. */ content: PropTypes.func.isRequired, /** A css class to be applied to the component's root element */ className: PropTypes.string, }; export default ClosablePopover;