UNPKG

@momentum-ui/react

Version:

Cisco Momentum UI framework for ReactJs applications

485 lines (386 loc) 16.2 kB
"use strict"; exports.__esModule = true; exports.default = void 0; var _react = _interopRequireDefault(require("react")); var _reactDom = _interopRequireDefault(require("react-dom")); var _propTypes = _interopRequireDefault(require("prop-types")); var _ = require("./.."); var _omit = _interopRequireDefault(require("lodash/omit")); var _excluded = ["children", "className", "content", "includeFocusOnHover", "overflowType", "popoverTrigger", "showArrow"]; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } var Popover = /*#__PURE__*/function (_React$Component) { _inheritsLoose(Popover, _React$Component); function Popover() { var _this; for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } _this = _React$Component.call.apply(_React$Component, [this].concat(args)) || this; _this.state = { isOpen: _this.props.startOpen || false, isHovering: _this.props.startOpen || false }; _this.delayedHide = function (e) { var _this$props = _this.props, delay = _this$props.delay, hideDelay = _this$props.hideDelay, onClose = _this$props.onClose; var isHovering = _this.state.isHovering; if (isHovering) return; if (_this.showTimerId) { clearTimeout(_this.showTimerId); _this.showTimerId = null; } var popoverHideTime = hideDelay ? hideDelay : delay; _this.hideTimerId = setTimeout(function () { _this.hideTimerId = null; _this.setState(function () { return { isOpen: false, isHovering: false }; }, onClose && onClose(e)); }, popoverHideTime); e && e.stopPropagation(); }; _this.delayedShow = function (e) { var _this$props2 = _this.props, delay = _this$props2.delay, showDelay = _this$props2.showDelay; if (_this.hideTimerId) { clearTimeout(_this.hideTimerId); _this.hideTimerId = null; } var popoverShowTime = showDelay ? showDelay : delay; _this.showTimerId = setTimeout(function () { _this.showTimerId = null; _this.setState(function () { return { isOpen: true, isHovering: true }; }); }, popoverShowTime); e && e.stopPropagation(); }; _this.handleClose = function (e) { var onClose = _this.props.onClose; var keyCode = e.keyCode; //allow to copy text on popover if (!(keyCode === 17 || keyCode === 91 || keyCode === 67)) { _this.setState({ isOpen: false }, onClose && onClose(e)); } }; _this.handleHide = function (e) { _this.setState({ isHovering: false }, function () { return _this.delayedHide(e); }); }; _this.handleMouseEnter = function (e) { var children = _this.props.children; children.props.onMouseEnter && children.props.onMouseEnter(e); return !_this.showTimerId && !_this.state.isOpen && _this.delayedShow(e); }; _this.delayCheckHover = function (e) { var _this$props3 = _this.props, hoverDelay = _this$props3.hoverDelay, popoverTrigger = _this$props3.popoverTrigger; var delay = popoverTrigger === 'MouseEnter' ? hoverDelay : 0; e.persist(); _this.setState({ isHovering: false }, function () { _this.delayCheckHoverTimerId && clearTimeout(_this.delayCheckHoverTimerId); _this.delayCheckHoverTimerId = setTimeout(function () { return _this.delayedHide(e); }, delay); }); }; _this.handleMouseLeave = function (e) { var children = _this.props.children; if (_this.hasFocus) { return false; } if (_this.showTimerId) { clearTimeout(_this.showTimerId); _this.showTimerId = null; } children.props.onMouseLeave && children.props.onMouseLeave(e); return !_this.hideTimerId && _this.state.isOpen && _this.delayCheckHover(e); }; _this.handleBlur = function (e) { var children = _this.props.children; _this.hasFocus = false; children.props.onBlur && children.props.onBlur(e); _this.handleMouseLeave(e); }; _this.handleClick = function (e) { var _this$props4 = _this.props, children = _this$props4.children, doesAnchorToggle = _this$props4.doesAnchorToggle; var isOpen = _this.state.isOpen; _this.hasFocus = true; children.props.onClick && children.props.onClick(e); if (!_this.showTimerId) { return !isOpen ? _this.delayedShow(e) : doesAnchorToggle && _this.handleBlur(e); } }; _this.handleFocus = function (e) { var children = _this.props.children; var isOpen = _this.state.isOpen; _this.hasFocus = true; children.props.onFocus && children.props.onFocus(e); if (!_this.showTimerId) { return !isOpen ? _this.delayedShow(e) : _this.handleBlur(e); } }; _this.handleKeyDownTrigger = function (e) { switch (e.which) { case 13: // ENTER e.preventDefault(); e.stopPropagation(); e.persist(); // Open Popover _this.setState({ isHovering: true }, function () { return _this.delayedShow(e); }); break; case 27: // ESC e.persist(); e.stopPropagation(); // Close Popover _this.handleHide(e); // Focus on trigger if (e.target) { e.target.focus(); } break; } }; _this.handleKeyDownEventOverlay = function (e) { if (_this.state.isOpen && _this.overlay && _this.anchorRef) { var eventOverlay = _reactDom.default.findDOMNode(_this.overlay); var trigger = _reactDom.default.findDOMNode(_this.anchorRef); var tabbableElements = eventOverlay.querySelectorAll('[tabindex="0"]'); switch (e.which) { case 9: if (tabbableElements.length) { if (_this.props.closeOnFocusLeavesContent) { // if closeOnFocusLeavesContent = true if (e.shiftKey) { // SHIFT + TAB // If first interactable element in EventOverlay, hide the popover if (document.activeElement === tabbableElements[0]) { _this.handleHide(e); } } else { // TAB // If last interactable element in EventOverlay, hide the popover if (document.activeElement === tabbableElements[tabbableElements.length - 1]) { e.preventDefault(); e.stopPropagation(); _this.handleHide(e); trigger.focus(); } } } else { // if closeOnFocusLeavesContent = false // Intent is for TAB and SHIFT+TAB to trap the user inside the dialog (AKA popover) if (e.shiftKey) { // SHIFT + TAB // If first interactable element in EventOverlay, go to the last interatable element if (document.activeElement === tabbableElements[0]) { e.preventDefault(); e.stopPropagation(); tabbableElements[tabbableElements.length - 1].focus(); } } else { // TAB // If last interactable element in EventOverlay, go to the first interatable element if (document.activeElement === tabbableElements[tabbableElements.length - 1]) { e.preventDefault(); e.stopPropagation(); tabbableElements[0].focus(); } } } } break; case 27: // ESC e.stopPropagation(); _this.handleHide(e); trigger.focus(); break; } } }; return _this; } var _proto = Popover.prototype; _proto.componentDidMount = function componentDidMount() { this.props.startOpen && this.forceUpdate(); }; _proto.componentDidUpdate = function componentDidUpdate(prevProps, prevState) { // focus on the first button in the EventOverlay var isOpen = this.state.isOpen; var autoFocusOnFirstElt = this.props.autoFocusOnFirstElt; if (autoFocusOnFirstElt && isOpen && !prevState.isOpen && this.overlay) { var eventOverlay = _reactDom.default.findDOMNode(this.overlay); if (eventOverlay) { var firstTabbableElement = eventOverlay.querySelector('[tabindex="0"]'); if (firstTabbableElement) firstTabbableElement.focus(); } } }; _proto.componentWillUnmount = function componentWillUnmount() { this.hideTimerId && clearTimeout(this.hideTimerId); this.showTimerId && clearTimeout(this.showTimerId); this.delayCheckHoverTimerId && clearTimeout(this.delayCheckHoverTimerId); }; _proto.render = function render() { var _this2 = this; var isOpen = this.state.isOpen; var _this$props5 = this.props, children = _this$props5.children, className = _this$props5.className, content = _this$props5.content, includeFocusOnHover = _this$props5.includeFocusOnHover, overflowType = _this$props5.overflowType, popoverTrigger = _this$props5.popoverTrigger, showArrow = _this$props5.showArrow, props = _objectWithoutPropertiesLoose(_this$props5, _excluded); var otherProps = (0, _omit.default)(_extends({}, props), ['autoFocusOnFirstElt', 'closeOnFocusLeavesContent', 'delay', 'doesAnchorToggle', 'hideDelay', 'hoverDelay', 'onClose', 'showDelay', 'startOpen']); var getTriggers = function getTriggers() { var triggerProps = {}; triggerProps.ref = function (ele) { return _this2.anchorRef = ele; }; triggerProps.key = 'child-1'; switch (popoverTrigger) { case 'MouseEnter': triggerProps.onMouseEnter = _this2.handleMouseEnter; triggerProps.onMouseLeave = _this2.handleMouseLeave; triggerProps.onFocus = includeFocusOnHover ? _this2.handleFocus : undefined; triggerProps.onBlur = includeFocusOnHover ? _this2.handleBlur : undefined; if (!includeFocusOnHover) { triggerProps.onKeyDown = _this2.handleKeyDownTrigger; } break; case 'Click': triggerProps.onClick = _this2.handleClick; triggerProps.onBlur = null; triggerProps.onFocus = null; triggerProps.onMouseEnter = null; triggerProps.onMouseLeave = null; break; case 'Focus': triggerProps.onFocus = _this2.handleFocus; triggerProps.onBlur = _this2.handleBlur; triggerProps.onMouseEnter = null; triggerProps.onMouseLeave = null; break; case 'None': triggerProps.onClick = null; triggerProps.onFocus = null; triggerProps.onBlur = null; triggerProps.onMouseEnter = null; triggerProps.onMouseLeave = null; break; } return triggerProps; }; var anchorWithTriggers = function anchorWithTriggers() { return children && /*#__PURE__*/_react.default.cloneElement(children, getTriggers()); }; return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, anchorWithTriggers(), isOpen && /*#__PURE__*/_react.default.createElement(_.EventOverlay, _extends({ anchorNode: this.anchorRef, className: className, close: this.handleClose, isOpen: isOpen, ref: function ref(_ref) { return _this2.overlay = _ref; }, showArrow: showArrow, style: { overflow: overflowType } }, popoverTrigger === 'MouseEnter' && { onMouseEnter: function onMouseEnter() { _this2.setState({ isHovering: true, isOpen: true }); }, onMouseLeave: function onMouseLeave(e) { e.persist(); _this2.handleHide(e); }, onKeyDown: !includeFocusOnHover ? this.handleKeyDownEventOverlay : undefined }, popoverTrigger === 'Focus' && { allowClickAway: false }, otherProps), content)); }; return Popover; }(_react.default.Component); Popover.propTypes = { /** @prop Focus on the first interactable (tabindex="0") element in the popover | false */ autoFocusOnFirstElt: _propTypes.default.bool, /** @prop Children node that should be the popover trigger(this should be a stateful component) */ children: _propTypes.default.element.isRequired, /** @prop Optional CSS class names which goes over popover container | '' */ className: _propTypes.default.string, /** @prop Applies to TAB and SHIFT+TAB, when either makes the focus leave the EventOverlay | false */ closeOnFocusLeavesContent: _propTypes.default.bool, /** @prop The content that goes into the popover */ content: _propTypes.default.oneOfType([_propTypes.default.element, _propTypes.default.node]).isRequired, /** @prop The delay for popover on hover, click, focus (hide/show) | 0 */ delay: _propTypes.default.number, /** @prop Boolean for whether the Anchor Toggles the Popover | true */ doesAnchorToggle: _propTypes.default.bool, /** @prop The hide delay for popover on hover, click, focus | 0 */ hideDelay: _propTypes.default.number, /** @prop The hover delay for checking whether we are still hovering before closing | 500 */ hoverDelay: _propTypes.default.number, /** @prop Optional prop that determines whether Focus triggers apply to MouseEnter | true */ includeFocusOnHover: _propTypes.default.bool, /** @prop Callback function that will execute on close | null */ onClose: _propTypes.default.func, /** @prop Optional prop that controls overflow css style of EventOverlay | 'auto' */ overflowType: _propTypes.default.string, /** @prop Event that will trigger popover appearance | 'MouseEnter' */ popoverTrigger: _propTypes.default.oneOf(['MouseEnter', 'Click', 'Focus', 'None']), /** @prop Boolean for whether the Arrow should show | true */ showArrow: _propTypes.default.bool, /** @prop The show delay for popover on hover, click, focus | 0 */ showDelay: _propTypes.default.number, /** @prop Should Popover start open | false */ startOpen: _propTypes.default.bool }; Popover.defaultProps = { autoFocusOnFirstElt: false, className: '', closeOnFocusLeavesContent: false, delay: 0, doesAnchorToggle: true, hideDelay: 0, hoverDelay: 500, includeFocusOnHover: true, onClose: null, overflowType: 'auto', popoverTrigger: 'MouseEnter', showArrow: true, showDelay: 0, startOpen: false }; Popover.displayName = 'Popover'; var _default = Popover; exports.default = _default;