UNPKG

reactstrap

Version:
163 lines (147 loc) 4.11 kB
import React from 'react'; import PropTypes from 'prop-types'; import ReactDOM from 'react-dom'; import classNames from 'classnames'; import { Popper } from 'react-popper'; import { DropdownContext } from './DropdownContext'; import { mapToCssModules, tagPropType, targetPropType, getTarget, deprecated, } from './utils'; const propTypes = { tag: tagPropType, children: PropTypes.node.isRequired, dark: PropTypes.bool, end: PropTypes.bool, /** Flips the menu to the opposite side if there is not enough space to fit */ flip: PropTypes.bool, modifiers: PropTypes.array, className: PropTypes.string, cssModule: PropTypes.object, style: PropTypes.object, persist: PropTypes.bool, strategy: PropTypes.string, container: targetPropType, /** Update popper layout when a click event comes up. This leverages event bubbling. */ updateOnSelect: PropTypes.bool, right: deprecated(PropTypes.bool, 'Please use "end" instead.'), }; const directionPositionMap = { up: 'top', left: 'left', right: 'right', start: 'left', end: 'right', down: 'bottom', }; class DropdownMenu extends React.Component { getRole() { if (this.context.menuRole === 'listbox') { return 'listbox'; } return 'menu'; } render() { const { className, cssModule, dark, end, right, tag = 'div', flip = true, modifiers = [], persist, strategy, container, updateOnSelect, ...attrs } = this.props; const classes = mapToCssModules( classNames(className, 'dropdown-menu', { 'dropdown-menu-dark': dark, 'dropdown-menu-end': end || right, show: this.context.isOpen, }), cssModule, ); const Tag = tag; if (persist || (this.context.isOpen && !this.context.inNavbar)) { const position1 = directionPositionMap[this.context.direction] || 'bottom'; const position2 = end || right ? 'end' : 'start'; const poperPlacement = `${position1}-${position2}`; const poperModifiers = [ ...modifiers, { name: 'flip', enabled: !!flip, }, ]; const persistStyles = {}; if (persist) { persistStyles.display = 'block'; persistStyles.visibility = this.context.isOpen ? 'visible' : 'hidden'; } const popper = ( <Popper placement={poperPlacement} modifiers={poperModifiers} strategy={strategy} > {({ ref, style, placement, update }) => { let combinedStyle = { ...this.props.style, ...persistStyles, ...style, }; const handleRef = (tagRef) => { // Send the ref to `react-popper` ref(tagRef); // Send the ref to the parent Dropdown so that clicks outside // it will cause it to close const { onMenuRef } = this.context; if (onMenuRef) onMenuRef(tagRef); }; return ( <Tag tabIndex="-1" role={this.getRole()} ref={handleRef} {...attrs} style={combinedStyle} aria-hidden={!this.context.isOpen} className={classes} data-popper-placement={placement} onClick={() => updateOnSelect && update()} /> ); }} </Popper> ); if (container) { return ReactDOM.createPortal(popper, getTarget(container)); } return popper; } const { onMenuRef } = this.context; return ( <Tag tabIndex="-1" role={this.getRole()} {...attrs} ref={onMenuRef} aria-hidden={!this.context.isOpen} className={classes} data-popper-placement={attrs.placement} data-bs-popper="static" /> ); } } DropdownMenu.propTypes = propTypes; DropdownMenu.contextType = DropdownContext; export default DropdownMenu;