UNPKG

@momentum-ui/react-collaboration

Version:

Cisco Momentum UI Framework for React Collaboration Applications

378 lines 18.2 kB
/** @component popover */ var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; import React from 'react'; import ReactDOM from 'react-dom'; import PropTypes from 'prop-types'; import EventOverlay from '../EventOverlay'; import omit from 'lodash/omit'; import { isMRv2Button } from '../../helpers/verifyTypes'; /** * @deprecated - Components in the legacy folder (/src/legacy) are deprecated. Please use a component from the components folder (/src/components) instead. Legacy components may not follow accessibility standards. **/ var Popover = /** @class */ (function (_super) { __extends(Popover, _super); function Popover() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.state = { isOpen: _this.props.startOpen || false, isHovering: _this.props.startOpen || false, }; _this.delayedHide = function (e) { var _a = _this.props, delay = _a.delay, hideDelay = _a.hideDelay, onClose = _a.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 _a = _this.props, delay = _a.delay, showDelay = _a.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); if (e && e.stopPropagation) { 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 _a = _this.props, hoverDelay = _a.hoverDelay, popoverTrigger = _a.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 _a = _this.props, children = _a.children, doesAnchorToggle = _a.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); } }; // Handle keydown for the trigger element _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.findDOMNode(_this.overlay); var trigger = ReactDOM.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; } Popover.prototype.componentDidMount = function () { this.props.startOpen && this.forceUpdate(); }; Popover.prototype.componentDidUpdate = function (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.findDOMNode(this.overlay); if (eventOverlay) { var firstTabbableElement = eventOverlay.querySelector('[tabindex="0"]'); if (firstTabbableElement) firstTabbableElement.focus(); } } }; Popover.prototype.componentWillUnmount = function () { this.hideTimerId && clearTimeout(this.hideTimerId); this.showTimerId && clearTimeout(this.showTimerId); this.delayCheckHoverTimerId && clearTimeout(this.delayCheckHoverTimerId); }; Popover.prototype.render = function () { var _this = this; var isOpen = this.state.isOpen; var _a = this.props, children = _a.children, className = _a.className, content = _a.content, includeFocusOnHover = _a.includeFocusOnHover, overflowType = _a.overflowType, popoverTrigger = _a.popoverTrigger, showArrow = _a.showArrow, props = __rest(_a, ["children", "className", "content", "includeFocusOnHover", "overflowType", "popoverTrigger", "showArrow"]); var otherProps = omit(__assign({}, props), [ 'autoFocusOnFirstElt', 'closeOnFocusLeavesContent', 'delay', 'doesAnchorToggle', 'hideDelay', 'hoverDelay', 'onClose', 'showDelay', 'startOpen', ]); var getTriggers = function () { var triggerProps = {}; triggerProps.ref = function (ele) { return (_this.anchorRef = ele); }; triggerProps.key = 'child-1'; switch (popoverTrigger) { case 'MouseEnter': triggerProps.onMouseEnter = _this.handleMouseEnter; triggerProps.onMouseLeave = _this.handleMouseLeave; triggerProps.onFocus = includeFocusOnHover ? _this.handleFocus : undefined; triggerProps.onBlur = includeFocusOnHover ? _this.handleBlur : undefined; if (!includeFocusOnHover) { triggerProps.onKeyDown = _this.handleKeyDownTrigger; } break; case 'Click': if (isMRv2Button(children)) { triggerProps.onPress = _this.handleClick; } else { triggerProps.onClick = _this.handleClick; } triggerProps.onBlur = null; triggerProps.onFocus = null; triggerProps.onMouseEnter = null; triggerProps.onMouseLeave = null; break; case 'Focus': triggerProps.onFocus = _this.handleFocus; triggerProps.onBlur = _this.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 () { return children && React.cloneElement(children, getTriggers()); }; return (React.createElement(React.Fragment, null, anchorWithTriggers(), isOpen && (React.createElement(EventOverlay, __assign({ anchorNode: this.anchorRef, className: className, close: this.handleClose, isOpen: isOpen, ref: function (ref) { return (_this.overlay = ref); }, showArrow: showArrow, style: { overflow: overflowType } }, (popoverTrigger === 'MouseEnter' && { onMouseEnter: function () { _this.setState({ isHovering: true, isOpen: true }); }, onMouseLeave: function (e) { e.persist(); _this.handleHide(e); }, onKeyDown: !includeFocusOnHover ? this.handleKeyDownEventOverlay : undefined, }), (popoverTrigger === 'Focus' && { allowClickAway: false }), otherProps), content)))); }; return Popover; }(React.Component)); Popover.propTypes = { /** @prop Focus on the first interactable (tabindex="0") element in the popover | false */ autoFocusOnFirstElt: PropTypes.bool, /** @prop Children node that should be the popover trigger(this should be a stateful component) */ children: PropTypes.element.isRequired, /** @prop Optional CSS class names which goes over popover container | '' */ className: PropTypes.string, /** @prop Applies to TAB and SHIFT+TAB, when either makes the focus leave the EventOverlay | false */ closeOnFocusLeavesContent: PropTypes.bool, /** @prop The content that goes into the popover */ content: PropTypes.oneOfType([PropTypes.element, PropTypes.node]).isRequired, /** @prop The delay for popover on hover, click, focus (hide/show) | 0 */ delay: PropTypes.number, /** @prop Boolean for whether the Anchor Toggles the Popover | true */ doesAnchorToggle: PropTypes.bool, /** @prop The hide delay for popover on hover, click, focus | 0 */ hideDelay: PropTypes.number, /** @prop The hover delay for checking whether we are still hovering before closing | 500 */ hoverDelay: PropTypes.number, /** @prop Optional prop that determines whether Focus triggers apply to MouseEnter | true */ includeFocusOnHover: PropTypes.bool, /** @prop Callback function that will execute on close | null */ onClose: PropTypes.func, /** @prop Optional prop that controls overflow css style of EventOverlay | 'auto' */ overflowType: PropTypes.string, /** @prop Event that will trigger popover appearance | 'MouseEnter' */ popoverTrigger: PropTypes.oneOf(['MouseEnter', 'Click', 'Focus', 'None']), /** @prop Boolean for whether the Arrow should show | true */ showArrow: PropTypes.bool, /** @prop The show delay for popover on hover, click, focus | 0 */ showDelay: PropTypes.number, /** @prop Should Popover start open | false */ startOpen: PropTypes.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'; export default Popover; //# sourceMappingURL=index.js.map