import * as RadixMenu from '@radix-ui/react-dropdown-menu' import React from 'react' import { Check } from 'lucide-react' import { MenuItemActionType, type MenuItemType, MenuItemVariant } from './types' // eslint-disable-next-line import-x/no-cycle -- intentional recursion: MenuItem renders SubMenu which renders MenuItem import { SubMenu } from './SubMenu' import Block from '../Primitives/Block/Block' import Text from '../Text/Text' import { getMenuItemStateToken, type MenuItemSelectionStates, type MenuTokensType, } from './menu.tokens' import { useResponsiveTokens } from '../../hooks/useResponsiveTokens' import { Tooltip } from '../Tooltip' import { resolveMenuSelection, useMenuSelection, type MenuSelectionMode, type MenuSelectionStyle, } from './selection' const MenuSlot = ({ slot, isDecorative = true, }: { slot: React.ReactNode isDecorative?: boolean }) => { const content = isDecorative && React.isValidElement(slot) ? ( React.cloneElement(slot, { 'aria-hidden': 'true', } as React.HTMLAttributes) ) : isDecorative ? ( ) : ( slot ) return ( {content} ) } const CheckmarkIndicator = ({ menuTokens, disabled, }: { menuTokens: MenuTokensType disabled?: boolean }) => { const checkmark = menuTokens.item.checkmark const position = checkmark?.position ?? 'trailing' const width = checkmark?.width ?? 16 return ( ) } const getBgColor = ( state: MenuItemSelectionStates, menuTokens: MenuTokensType, item: MenuItemType ) => { const bg = menuTokens.item.backgroundColor // check for variant if (item.variant === MenuItemVariant.DEFAULT) { if (!item.disabled) { return getMenuItemStateToken(bg.default.enabled, state) } else { return getMenuItemStateToken(bg.default.disabled, state) } } else { // check for action type if (item.actionType === undefined) { item.actionType = MenuItemActionType.PRIMARY } if (item.actionType === MenuItemActionType.PRIMARY) { if (!item.disabled) { return getMenuItemStateToken(bg.action.primary.enabled, state) } else { return getMenuItemStateToken(bg.action.primary.disabled, state) } } else { if (!item.disabled) { return getMenuItemStateToken(bg.action.danger.enabled, state) } else { return getMenuItemStateToken(bg.action.danger.disabled, state) } } } } const getColor = ( state: MenuItemSelectionStates, menuTokens: MenuTokensType, item: MenuItemType ) => { const bg = menuTokens.item.option.color // check for variant if (item.variant === MenuItemVariant.DEFAULT) { if (!item.disabled) { return getMenuItemStateToken(bg.default.enabled, state) } else { return getMenuItemStateToken(bg.default.disabled, state) } } else { // check for action type if (item.actionType === undefined) { item.actionType = MenuItemActionType.PRIMARY } if (item.actionType === MenuItemActionType.PRIMARY) { if (!item.disabled) { return getMenuItemStateToken(bg.action.primary.enabled, state) } else { return getMenuItemStateToken(bg.action.primary.disabled, state) } } else { if (!item.disabled) { return getMenuItemStateToken(bg.action.danger.enabled, state) } else { return getMenuItemStateToken(bg.action.danger.disabled, state) } } } } const MenuItem = ({ item, idx, maxHeight, selectionStyle: groupSelectionStyle, selectionMode: groupSelectionMode, }: { item: MenuItemType idx: number maxHeight?: number selectionStyle?: MenuSelectionStyle selectionMode?: MenuSelectionMode }) => { const menuTokens = useResponsiveTokens('MENU') const { selectionStyle: menuSelectionStyle, selectionMode: menuSelectionMode, closeOnSelect, } = useMenuSelection() const isSubMenu = item.subMenu && (item.subMenu.length > 0 || typeof item.selected !== 'boolean') if (isSubMenu) { return ( ) } if (item.variant === undefined) { item.variant = MenuItemVariant.DEFAULT } const selection = resolveMenuSelection({ selected: item.selected, groupSelectionStyle, groupSelectionMode, menuSelectionStyle, menuSelectionMode, }) const useHighlight = selection.selectionStyle === 'highlight' && selection.isSelected const showCheckmark = selection.selectionStyle === 'checkmark' && selection.isSelected const checkmarkPosition = menuTokens.item.checkmark?.position ?? 'trailing' const showLeadingCheck = showCheckmark && checkmarkPosition === 'leading' const showTrailingCheck = showCheckmark && checkmarkPosition === 'trailing' const defaultState: MenuItemSelectionStates = useHighlight ? 'selected' : 'default' const handleSelect = (event: Event) => { if (item.disabled) return if (!closeOnSelect) { event.preventDefault() } item.onClick?.() } const menuItemContent = ( {showLeadingCheck && ( )} {item.slot1 && ( )} {item.label} {item.slot2 && ( )} {item.slot3 && ( )} {item.slot4 && ( )} {showTrailingCheck && ( )} {item.subLabel && ( {item.subLabel} )} ) // Wrap with tooltip if tooltip content is provided if (item.tooltip) { return ( {menuItemContent} ) } return menuItemContent } MenuItem.displayName = 'MenuItem' export default MenuItem