UNPKG

@activelylearn/material-ui

Version:

Material-UI's workspace package

214 lines (192 loc) 5.15 kB
// @inheritedComponent Popover import React from 'react'; import PropTypes from 'prop-types'; import ReactDOM from 'react-dom'; import getScrollbarSize from 'dom-helpers/util/scrollbarSize'; import withStyles from '../styles/withStyles'; import Popover from '../Popover'; import MenuList from '../MenuList'; const RTL_ORIGIN = { vertical: 'top', horizontal: 'right', }; const LTR_ORIGIN = { vertical: 'top', horizontal: 'left', }; export const styles = { paper: { // specZ: The maximum height of a simple menu should be one or more rows less than the view // height. This ensures a tappable area outside of the simple menu with which to dismiss // the menu. maxHeight: 'calc(100% - 96px)', // Add iOS momentum scrolling. WebkitOverflowScrolling: 'touch', }, }; class Menu extends React.Component { componentDidMount() { if (this.props.open) { this.focus(); } } getContentAnchorEl = () => { if (!this.menuList || !this.menuList.selectedItem) { return ReactDOM.findDOMNode(this.menuList).firstChild; } return ReactDOM.findDOMNode(this.menuList.selectedItem); }; menuList = undefined; focus = () => { if (this.menuList && this.menuList.selectedItem) { ReactDOM.findDOMNode(this.menuList.selectedItem).focus(); return; } const menuList = ReactDOM.findDOMNode(this.menuList); if (menuList && menuList.firstChild) { menuList.firstChild.focus(); } }; handleEnter = element => { const { theme } = this.props; const menuList = ReactDOM.findDOMNode(this.menuList); // Focus so the scroll computation of the Popover works as expected. this.focus(); // Let's ignore that piece of logic if users are already overriding the width // of the menu. if (menuList && element.clientHeight < menuList.clientHeight && !menuList.style.width) { const size = `${getScrollbarSize()}px`; menuList.style[theme.direction === 'rtl' ? 'paddingLeft' : 'paddingRight'] = size; menuList.style.width = `calc(100% + ${size})`; } if (this.props.onEnter) { this.props.onEnter(element); } }; handleListKeyDown = (event, key) => { if (key === 'tab') { event.preventDefault(); if (this.props.onClose) { this.props.onClose(event); } } }; render() { const { children, classes, MenuListProps, onEnter, PaperProps = {}, PopoverClasses, theme, ...other } = this.props; return ( <Popover getContentAnchorEl={this.getContentAnchorEl} classes={PopoverClasses} onEnter={this.handleEnter} anchorOrigin={theme.direction === 'rtl' ? RTL_ORIGIN : LTR_ORIGIN} transformOrigin={theme.direction === 'rtl' ? RTL_ORIGIN : LTR_ORIGIN} PaperProps={{ ...PaperProps, classes: { ...PaperProps.classes, root: classes.paper, }, }} {...other} > <MenuList data-mui-test="Menu" role="menu" onKeyDown={this.handleListKeyDown} {...MenuListProps} ref={node => { this.menuList = node; }} > {children} </MenuList> </Popover> ); } } Menu.propTypes = { /** * The DOM element used to set the position of the menu. */ anchorEl: PropTypes.object, /** * Menu contents, normally `MenuItem`s. */ children: PropTypes.node, /** * Override or extend the styles applied to the component. * See [CSS API](#css-api) below for more details. */ classes: PropTypes.object.isRequired, /** * Properties applied to the `MenuList` element. */ MenuListProps: PropTypes.object, /** * Callback fired when the component requests to be closed. * * @param {object} event The event source of the callback */ onClose: PropTypes.func, /** * Callback fired before the Menu enters. */ onEnter: PropTypes.func, /** * Callback fired when the Menu has entered. */ onEntered: PropTypes.func, /** * Callback fired when the Menu is entering. */ onEntering: PropTypes.func, /** * Callback fired before the Menu exits. */ onExit: PropTypes.func, /** * Callback fired when the Menu has exited. */ onExited: PropTypes.func, /** * Callback fired when the Menu is exiting. */ onExiting: PropTypes.func, /** * If `true`, the menu is visible. */ open: PropTypes.bool.isRequired, /** * @ignore */ PaperProps: PropTypes.object, /** * `classes` property applied to the `Popover` element. */ PopoverClasses: PropTypes.object, /** * @ignore */ theme: PropTypes.object.isRequired, /** * The length of the transition in `ms`, or 'auto' */ transitionDuration: PropTypes.oneOfType([ PropTypes.number, PropTypes.shape({ enter: PropTypes.number, exit: PropTypes.number }), PropTypes.oneOf(['auto']), ]), }; Menu.defaultProps = { transitionDuration: 'auto', }; export default withStyles(styles, { name: 'MuiMenu', withTheme: true })(Menu);