UNPKG

@material-ui/core

Version:

React components that implement Google's Material Design.

233 lines (199 loc) 6.09 kB
import _extends from "@babel/runtime/helpers/extends"; import _objectSpread from "@babel/runtime/helpers/objectSpread"; import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties"; // @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 = { /* Styles applied to the `Paper` component. */ paper: { // specZ: The maximum height of a simple menu should be one or more rows less than the view // height. This ensures a tapable 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 { constructor(...args) { super(...args); this.getContentAnchorEl = () => { if (!this.menuListRef || !this.menuListRef.selectedItemRef) { return ReactDOM.findDOMNode(this.menuListRef).firstChild; } return ReactDOM.findDOMNode(this.menuListRef.selectedItemRef); }; this.focus = () => { if (this.menuListRef && this.menuListRef.selectedItemRef) { ReactDOM.findDOMNode(this.menuListRef.selectedItemRef).focus(); return; } const menuList = ReactDOM.findDOMNode(this.menuListRef); if (menuList && menuList.firstChild) { menuList.firstChild.focus(); } }; this.handleEnter = element => { const { disableAutoFocusItem, theme } = this.props; const menuList = ReactDOM.findDOMNode(this.menuListRef); // Focus so the scroll computation of the Popover works as expected. if (disableAutoFocusItem !== true) { 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); } }; this.handleListKeyDown = (event, key) => { if (key === 'tab') { event.preventDefault(); if (this.props.onClose) { this.props.onClose(event); } } }; } componentDidMount() { if (this.props.open && this.props.disableAutoFocusItem !== true) { this.focus(); } } render() { const _this$props = this.props, { children, classes, disableAutoFocusItem, MenuListProps, onEnter, PaperProps = {}, PopoverClasses, theme } = _this$props, other = _objectWithoutProperties(_this$props, ["children", "classes", "disableAutoFocusItem", "MenuListProps", "onEnter", "PaperProps", "PopoverClasses", "theme"]); return React.createElement(Popover, _extends({ 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: _objectSpread({}, PaperProps, { classes: _objectSpread({}, PaperProps.classes, { root: classes.paper }) }) }, other), React.createElement(MenuList, _extends({ onKeyDown: this.handleListKeyDown }, MenuListProps, { ref: ref => { this.menuListRef = ref; } }), children)); } } Menu.propTypes = process.env.NODE_ENV !== "production" ? { /** * The DOM element used to set the position of the menu. */ anchorEl: PropTypes.oneOfType([PropTypes.object, PropTypes.func]), /** * 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, /** * If `true`, the selected / first menu item will not be auto focused. */ disableAutoFocusItem: PropTypes.bool, /** * Properties applied to the [`MenuList`](/api/menu-list/) 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`](/api/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 = { disableAutoFocusItem: false, transitionDuration: 'auto' }; export default withStyles(styles, { name: 'MuiMenu', withTheme: true })(Menu);