'use client'; import styles from '@components/DropdownMenu.module.css'; import * as React from 'react'; import ActionButton from '@components/ActionButton'; import ActionListItem from '@components/ActionListItem'; import ModalTrigger from '@components/ModalTrigger'; import { useHotkeys } from '@modules/hotkeys'; interface DropdownMenuItemProps { children: React.ReactNode; icon?: React.ReactNode; href?: string; target?: string; onClick?: () => void; modal?: any; modalProps?: Record; } interface DropdownMenuProps extends React.HTMLAttributes { onClose?: (event?: MouseEvent | TouchEvent | KeyboardEvent) => void; items?: DropdownMenuItemProps[]; } const DropdownMenu = React.forwardRef((props, ref) => { const { onClose, items, style, ...rest } = props; const menuRef = React.useRef(null); const setRef = React.useCallback( (node: HTMLDivElement | null) => { (menuRef as React.MutableRefObject).current = node; if (typeof ref === 'function') ref(node); else if (ref) (ref as React.MutableRefObject).current = node; }, [ref] ); const handleClose = React.useCallback(() => { if (onClose) onClose(); }, [onClose]); //NOTE(jimmylee): Fallback for when focus is outside the menu container. useHotkeys('Escape', handleClose); const handleKeyDown = (event: React.KeyboardEvent) => { const menu = menuRef.current; if (!menu) return; const menuItems = Array.from(menu.querySelectorAll('[role="menuitem"]')); if (menuItems.length === 0) return; const currentIndex = menuItems.indexOf(event.target as HTMLElement); switch (event.key) { case 'ArrowDown': { event.preventDefault(); event.stopPropagation(); const next = currentIndex < menuItems.length - 1 ? currentIndex + 1 : 0; menuItems[next].focus(); break; } case 'ArrowUp': { event.preventDefault(); event.stopPropagation(); const prev = currentIndex > 0 ? currentIndex - 1 : menuItems.length - 1; menuItems[prev].focus(); break; } case 'Enter': case ' ': { event.preventDefault(); event.stopPropagation(); if (currentIndex >= 0) { menuItems[currentIndex].click(); } break; } case 'Escape': { event.preventDefault(); event.stopPropagation(); handleClose(); break; } } }; return (
{items && items.map((each, index) => { if (each.modal) { return ( {each.children} ); } return ( { if (each.onClick) { each.onClick(); } if (onClose) { onClose(); } }} > {each.children} ); })}
); }); DropdownMenu.displayName = 'DropdownMenu'; export default DropdownMenu;