UNPKG

@equinor/eds-core-react

Version:

The React implementation of the Equinor Design System

104 lines (100 loc) 3.49 kB
import { forwardRef, useMemo, Children, cloneElement, useEffect, isValidElement } from 'react'; import styled from 'styled-components'; import { useMenu } from './Menu.context.js'; import { MenuItem } from './MenuItem.js'; import { MenuSection } from './MenuSection.js'; import { menu } from './Menu.tokens.js'; import { spacingsTemplate } from '@equinor/eds-utils'; import { jsx } from 'react/jsx-runtime'; const List = styled.div.withConfig({ displayName: "MenuList__List", componentId: "sc-104rzof-0" })(["position:relative;list-style:none;display:flex;flex-direction:column;margin:0;", " li:first-child{z-index:3;}"], spacingsTemplate(menu.spacings)); function isIndexable(item) { if (/*#__PURE__*/isValidElement(item) && !item.props.disabled && item.type === MenuItem) return true; return false; } function closeMenuOnClick(item) { if (/*#__PURE__*/isValidElement(item) && item.type === MenuItem && item.props.closeMenuOnClick !== false) return true; return false; } const MenuList = /*#__PURE__*/forwardRef(function MenuList({ addCloseMenuOnClickIndex, children, ...rest }, ref) { const { focusedIndex, setFocusedIndex, initialFocus } = useMenu(); let index = -1; const focusableIndexs = useMemo(() => [], []); const updatedChildren = useMemo(() => Children.map(children, child => { if (!child) return child; if (child.type === MenuSection) { index++; const menuSectionIndex = index; const updatedGrandChildren = Children.map(child.props.children, grandChild => { index++; if (isIndexable(grandChild)) focusableIndexs.push(index); return /*#__PURE__*/cloneElement(grandChild, { index }); }); return /*#__PURE__*/cloneElement(child, { index: menuSectionIndex }, updatedGrandChildren); } else { index++; if (isIndexable(child)) focusableIndexs.push(index); if (closeMenuOnClick(child)) { addCloseMenuOnClickIndex(index); } return /*#__PURE__*/cloneElement(child, { index }); } }), [children, focusableIndexs, index, addCloseMenuOnClickIndex]); const firstFocusIndex = focusableIndexs[0]; const lastFocusIndex = focusableIndexs[focusableIndexs.length - 1]; useEffect(() => { if (initialFocus === 'first') { setFocusedIndex(firstFocusIndex); } if (initialFocus === 'last') { setFocusedIndex(lastFocusIndex); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [initialFocus, firstFocusIndex, lastFocusIndex]); const handleMenuItemChange = (direction, fallbackIndex) => { const i = direction === 'down' ? 1 : -1; const currentFocus = focusableIndexs.indexOf(focusedIndex); const nextMenuItem = focusableIndexs[currentFocus + i]; const nextFocusedIndex = typeof nextMenuItem === 'undefined' ? fallbackIndex : nextMenuItem; setFocusedIndex(nextFocusedIndex); }; const handleKeyPress = event => { const { key } = event; event.stopPropagation(); if (key === 'ArrowDown') { event.preventDefault(); handleMenuItemChange('down', firstFocusIndex); } if (key === 'ArrowUp') { event.preventDefault(); handleMenuItemChange('up', lastFocusIndex); } }; return /*#__PURE__*/jsx(List, { onKeyDown: handleKeyPress, role: "menu", ...rest, ref: ref, children: updatedChildren }); }); // MenuList.displayName = 'EdsMenuList' export { MenuList };