mdc-react
Version:
Material Components for the web implemented in React
94 lines (78 loc) • 2.59 kB
JSX
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;