@momentum-ui/react-collaboration
Version:
Cisco Momentum UI Framework for React Collaboration Applications
378 lines • 18.2 kB
JavaScript
/** @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