UNPKG

mdc-react

Version:

Material Components for the web implemented in React

94 lines (78 loc) 2.59 kB
import { forwardRef, useState, useEffect, useCallback, useRef, Children } from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import { useUpdated } from '../hooks'; import { Clone, create } from '../component'; import List from '../list'; import { cssClasses } from './constants'; import MenuItem from './MenuItem'; import MenuSurface from './MenuSurface'; const Menu = forwardRef(({ open, items, listProps = {}, children = items?.map(item => create(MenuItem, item)), className, ...props }, ref) => { const listRef = useRef(); const [focusedChildIndex, setFocusedChildIndex] = useState(0); useEffect(() => { if (open) { setFocusedChildIndex( Children.toArray(children).findIndex(i => i.props.selected) ); } }, [open, children]); useUpdated(() => { if (open) { listRef.current.children[focusedChildIndex]?.focus(); } }, [focusedChildIndex, open]); const handleKeyDown = useCallback(event => { event.preventDefault(); if (event.key === 'ArrowDown') { setFocusedChildIndex(index => { const nextIndex = index + 1; return nextIndex < listRef.current.children.length ? nextIndex : index; }); } else if (event.key === 'ArrowUp') { setFocusedChildIndex(index => { const nextIndex = index - 1; return nextIndex >= 0 ? nextIndex : index; }); } }, []); const classNames = classnames(cssClasses.ROOT, className); return ( <MenuSurface ref={ref} open={open} className={classNames} onKeyDown={handleKeyDown} {...props} > <List ref={listRef} role="menu" aria-hidden="true" aria-orientation="vertical" tabIndex={open ? 0 : -1} {...listProps} > {Children.map(children, (item, index) => <Clone component={item} tabIndex={(index === focusedChildIndex) ? 0 : -1} /> )} </List> </MenuSurface> ); }); Menu.displayName = 'MDCMenu'; Menu.propTypes = { ...MenuSurface.propTypes, listProps: PropTypes.object }; export default Menu;