@coreui/react-pro
Version:
UI Components Library for React.js
229 lines (225 loc) • 9.93 kB
JavaScript
'use strict';
var tslib_es6 = require('../../node_modules/tslib/tslib.es6.js');
var React = require('react');
var PropTypes = require('prop-types');
var index = require('../../_virtual/index.js');
var CDropdownContext = require('./CDropdownContext.js');
var usePopper = require('../../hooks/usePopper.js');
var getNextActiveElement = require('../../utils/getNextActiveElement.js');
var isRTL = require('../../utils/isRTL.js');
var useForkedRef = require('../../hooks/useForkedRef.js');
var props = require('../../props.js');
var utils = require('./utils.js');
var CFocusTrap = require('../focus-trap/CFocusTrap.js');
const CDropdown = React.forwardRef((_a, ref) => {
var { children, alignment, as = 'div', autoClose = true, className, container, dark, direction, offset = [0, 2], onHide, onShow, placement = 'bottom-start', popper = true, popperConfig, portal = false, reference = 'toggle', variant = 'btn-group', visible = false } = _a, rest = tslib_es6.__rest(_a, ["children", "alignment", "as", "autoClose", "className", "container", "dark", "direction", "offset", "onHide", "onShow", "placement", "popper", "popperConfig", "portal", "reference", "variant", "visible"]);
const dropdownRef = React.useRef(null);
const dropdownMenuRef = React.useRef(null);
const forkedRef = useForkedRef.useForkedRef(ref, dropdownRef);
const [dropdownToggleElement, setDropdownToggleElement] = React.useState(null);
const [pendingKeyDownEvent, setPendingKeyDownEvent] = React.useState(null);
const [_visible, setVisible] = React.useState(visible);
const { initPopper, destroyPopper } = usePopper.usePopper();
const dropdownToggleRef = React.useCallback((node) => {
if (node) {
setDropdownToggleElement(node);
}
}, []);
const allowPopperUse = popper && typeof alignment !== 'object';
const Component = variant === 'nav-item' ? 'li' : as;
const computedPopperConfig = React.useMemo(() => {
const defaultPopperConfig = {
modifiers: [
{
name: 'offset',
options: {
offset,
},
},
],
placement: utils.getPlacement(placement, direction, alignment, isRTL.default(dropdownMenuRef.current)),
};
return Object.assign(Object.assign({}, defaultPopperConfig), (typeof popperConfig === 'function' ? popperConfig(defaultPopperConfig) : popperConfig));
}, [offset, placement, direction, alignment, popperConfig]);
React.useEffect(() => {
if (visible) {
handleShow();
}
else {
handleHide();
}
}, [visible]);
React.useEffect(() => {
return () => {
if (_visible) {
handleHide();
}
};
}, []);
React.useEffect(() => {
const referenceElement = utils.getReferenceElement(reference, dropdownToggleElement, dropdownRef);
const menuElement = dropdownMenuRef.current;
if (allowPopperUse && menuElement && referenceElement && _visible) {
initPopper(referenceElement, menuElement, computedPopperConfig);
}
}, [dropdownToggleElement, reference]);
React.useEffect(() => {
if (pendingKeyDownEvent !== null) {
handleKeydown(pendingKeyDownEvent);
setPendingKeyDownEvent(null);
}
}, [pendingKeyDownEvent]);
const handleHide = React.useCallback(() => {
setVisible(false);
const menuElement = dropdownMenuRef.current;
const toggleElement = dropdownToggleElement;
if (allowPopperUse) {
destroyPopper();
}
menuElement === null || menuElement === void 0 ? void 0 : menuElement.removeEventListener('keydown', handleKeydown);
toggleElement === null || toggleElement === void 0 ? void 0 : toggleElement.removeEventListener('keydown', handleKeydown);
window.removeEventListener('click', handleClick);
window.removeEventListener('keyup', handleKeyup);
onHide === null || onHide === void 0 ? void 0 : onHide();
}, [allowPopperUse, dropdownToggleElement, destroyPopper, onHide]);
const handleKeydown = React.useCallback((event) => {
if (!dropdownMenuRef.current) {
return;
}
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
event.preventDefault();
const target = event.target;
const items = [
...dropdownMenuRef.current.querySelectorAll('.dropdown-item:not(.disabled):not(:disabled)'),
];
getNextActiveElement.default(items, target, event.key === 'ArrowDown', true).focus();
}
}, []);
const handleKeyup = React.useCallback((event) => {
if (autoClose === false) {
return;
}
if (event.key === 'Escape') {
handleHide();
dropdownToggleElement === null || dropdownToggleElement === void 0 ? void 0 : dropdownToggleElement.focus();
}
}, [autoClose, dropdownToggleElement, handleHide]);
const handleClick = React.useCallback((event) => {
if (!dropdownToggleElement || !dropdownMenuRef.current) {
return;
}
if (event.button === 2) {
return;
}
const composedPath = event.composedPath();
const isOnToggle = composedPath.includes(dropdownToggleElement);
const isOnMenu = composedPath.includes(dropdownMenuRef.current);
if (isOnToggle) {
return;
}
const target = event.target;
const FORM_TAG_RE = /^(input|select|option|textarea|form)$/i;
if (isOnMenu && target && FORM_TAG_RE.test(target.tagName)) {
return;
}
if (autoClose === true ||
(autoClose === 'inside' && isOnMenu) ||
(autoClose === 'outside' && !isOnMenu)) {
handleHide();
}
}, [autoClose, dropdownToggleElement, handleHide]);
const handleShow = React.useCallback((event) => {
const menuElement = dropdownMenuRef.current;
const referenceElement = utils.getReferenceElement(reference, dropdownToggleElement, dropdownRef);
const toggleElement = dropdownToggleElement;
if (menuElement && referenceElement && toggleElement) {
setVisible(true);
if (allowPopperUse) {
initPopper(referenceElement, menuElement, computedPopperConfig);
}
toggleElement.focus();
toggleElement.addEventListener('keydown', handleKeydown);
menuElement.addEventListener('keydown', handleKeydown);
window.addEventListener('click', handleClick);
window.addEventListener('keyup', handleKeyup);
if (event && (event.key === 'ArrowDown' || event.key === 'ArrowUp')) {
setPendingKeyDownEvent(event);
}
onShow === null || onShow === void 0 ? void 0 : onShow();
}
}, [
allowPopperUse,
computedPopperConfig,
dropdownToggleElement,
reference,
handleClick,
handleKeydown,
handleKeyup,
initPopper,
onShow,
]);
const contextValues = React.useMemo(() => ({
alignment,
container,
dark,
dropdownMenuRef,
dropdownToggleRef,
handleHide,
handleShow,
popper: allowPopperUse,
portal,
variant,
visible: _visible,
}), [
alignment,
container,
dark,
dropdownMenuRef,
dropdownToggleRef,
handleHide,
handleShow,
allowPopperUse,
portal,
variant,
_visible,
]);
return (React.createElement(CDropdownContext.CDropdownContext.Provider, { value: contextValues },
React.createElement(CFocusTrap.CFocusTrap, { active: portal && _visible, additionalContainer: dropdownMenuRef, restoreFocus: true }, variant === 'input-group' ? (React.createElement(React.Fragment, null, children)) : (React.createElement(Component, Object.assign({ className: index.default(variant === 'nav-item' ? 'nav-item dropdown' : variant, {
'dropdown-center': direction === 'center',
'dropup dropup-center': direction === 'dropup-center',
[`${direction}`]: direction && direction !== 'center' && direction !== 'dropup-center',
}, className) }, rest, { ref: forkedRef }), children)))));
});
const alignmentDirection = PropTypes.oneOf(['start', 'end']);
CDropdown.propTypes = {
alignment: PropTypes.oneOfType([
alignmentDirection,
PropTypes.shape({ xs: alignmentDirection.isRequired }),
PropTypes.shape({ sm: alignmentDirection.isRequired }),
PropTypes.shape({ md: alignmentDirection.isRequired }),
PropTypes.shape({ lg: alignmentDirection.isRequired }),
PropTypes.shape({ xl: alignmentDirection.isRequired }),
PropTypes.shape({ xxl: alignmentDirection.isRequired }),
]),
as: PropTypes.elementType,
autoClose: PropTypes.oneOfType([
PropTypes.bool,
PropTypes.oneOf(['inside', 'outside']),
]),
children: PropTypes.node,
className: PropTypes.string,
dark: PropTypes.bool,
direction: PropTypes.oneOf(['center', 'dropup', 'dropup-center', 'dropend', 'dropstart']),
offset: PropTypes.any, // TODO: find good proptype
onHide: PropTypes.func,
onShow: PropTypes.func,
placement: props.placementPropType,
popper: PropTypes.bool,
popperConfig: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
portal: PropTypes.bool,
variant: PropTypes.oneOf(['btn-group', 'dropdown', 'input-group', 'nav-item']),
visible: PropTypes.bool,
};
CDropdown.displayName = 'CDropdown';
exports.CDropdown = CDropdown;
//# sourceMappingURL=CDropdown.js.map