UNPKG

@material-ui/core

Version:

React components that implement Google's Material Design.

266 lines (231 loc) 8 kB
import _extends from "@babel/runtime/helpers/esm/extends"; import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties"; import React from 'react'; import PropTypes from 'prop-types'; import clsx from 'clsx'; import withStyles from '../styles/withStyles'; import Popover from '../Popover'; import MenuList from '../MenuList'; import warning from 'warning'; import ReactDOM from 'react-dom'; import { setRef } from '../utils/reactHelpers'; var RTL_ORIGIN = { vertical: 'top', horizontal: 'right' }; var LTR_ORIGIN = { vertical: 'top', horizontal: 'left' }; export var 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' }, /* Styles applied to the `List` component via `MenuList`. */ list: { // We disable the focus ring for mouse, touch and keyboard users. outline: 'none' } }; var Menu = React.forwardRef(function Menu(props, ref) { var autoFocusProp = props.autoFocus, children = props.children, classes = props.classes, _props$disableAutoFoc = props.disableAutoFocusItem, disableAutoFocusItem = _props$disableAutoFoc === void 0 ? false : _props$disableAutoFoc, _props$MenuListProps = props.MenuListProps, MenuListProps = _props$MenuListProps === void 0 ? {} : _props$MenuListProps, onClose = props.onClose, onEntering = props.onEntering, open = props.open, _props$PaperProps = props.PaperProps, PaperProps = _props$PaperProps === void 0 ? {} : _props$PaperProps, PopoverClasses = props.PopoverClasses, theme = props.theme, _props$transitionDura = props.transitionDuration, transitionDuration = _props$transitionDura === void 0 ? 'auto' : _props$transitionDura, _props$variant = props.variant, variant = _props$variant === void 0 ? 'selectedMenu' : _props$variant, other = _objectWithoutProperties(props, ["autoFocus", "children", "classes", "disableAutoFocusItem", "MenuListProps", "onClose", "onEntering", "open", "PaperProps", "PopoverClasses", "theme", "transitionDuration", "variant"]); var autoFocus = (autoFocusProp !== undefined ? autoFocusProp : !disableAutoFocusItem) && open; var menuListActionsRef = React.useRef(null); var firstValidItemRef = React.useRef(null); var firstSelectedItemRef = React.useRef(null); var getContentAnchorEl = function getContentAnchorEl() { return firstSelectedItemRef.current || firstValidItemRef.current; }; var handleEntering = function handleEntering(element) { if (menuListActionsRef.current) { menuListActionsRef.current.adjustStyleForScrollbar(element, theme); } if (onEntering) { onEntering(element); } }; var handleListKeyDown = function handleListKeyDown(event) { if (event.key === 'Tab') { event.preventDefault(); if (onClose) { onClose(event, 'tabKeyDown'); } } }; var firstValidElementIndex = null; var firstSelectedIndex = null; var items = React.Children.map(children, function (child, index) { if (!React.isValidElement(child)) { return null; } process.env.NODE_ENV !== "production" ? warning(child.type !== React.Fragment, ["Material-UI: the Menu component doesn't accept a Fragment as a child.", 'Consider providing an array instead.'].join('\n')) : void 0; if (firstValidElementIndex === null) { firstValidElementIndex = index; } var newChildProps = null; if (variant !== "menu" && firstSelectedIndex === null && child.props.selected && !child.props.disabled) { firstSelectedIndex = index; newChildProps = {}; if (autoFocus) { newChildProps.autoFocus = true; } if (child.props.tabIndex === undefined) { newChildProps.tabIndex = 0; } newChildProps.ref = function (instance) { // #StrictMode ready firstSelectedItemRef.current = ReactDOM.findDOMNode(instance); setRef(child.ref, instance); }; } else if (index === firstValidElementIndex) { newChildProps = { ref: function ref(instance) { // #StrictMode ready firstValidItemRef.current = ReactDOM.findDOMNode(instance); setRef(child.ref, instance); } }; } if (newChildProps !== null) { return React.cloneElement(child, newChildProps); } return child; }); return React.createElement(Popover, _extends({ getContentAnchorEl: getContentAnchorEl, classes: PopoverClasses, onClose: onClose, onEntering: handleEntering, anchorOrigin: theme.direction === 'rtl' ? RTL_ORIGIN : LTR_ORIGIN, transformOrigin: theme.direction === 'rtl' ? RTL_ORIGIN : LTR_ORIGIN, PaperProps: _extends({}, PaperProps, { classes: _extends({}, PaperProps.classes, { root: classes.paper }) }), open: open, ref: ref, transitionDuration: transitionDuration }, other), React.createElement(MenuList, _extends({ onKeyDown: handleListKeyDown, actions: menuListActionsRef, autoFocus: autoFocus && firstSelectedIndex === null }, MenuListProps, { className: clsx(classes.list, MenuListProps.className) }), items)); }); process.env.NODE_ENV !== "production" ? Menu.propTypes = { /** * The DOM element used to set the position of the menu. */ anchorEl: PropTypes.oneOfType([PropTypes.object, PropTypes.func]), /** * If `true` (default), the menu list (possibly a particular item depending on the menu variant) will receive focus on open. */ autoFocus: PropTypes.bool, /** * Menu contents, normally `MenuItem`s. */ children: PropTypes.node, /** * Override or extend the styles applied to the component. * See [CSS API](#css) below for more details. */ classes: PropTypes.object.isRequired, /** * Same as `autoFocus=false`. * @deprecated Use `autoFocus` instead */ disableAutoFocusItem: PropTypes.bool, /** * Props 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 * @param {string} reason Can be:`"escapeKeyDown"`, `"backdropClick"`, `"tabKeyDown"` */ 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` prop 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'])]), /** * The variant to use. Use `menu` to prevent selected items from impacting the initial focus * and the vertical alignment relative to the anchor element. */ variant: PropTypes.oneOf(['menu', 'selectedMenu']) } : void 0; export default withStyles(styles, { name: 'MuiMenu', withTheme: true })(Menu);