// Copyright (c) Meta Platforms, Inc. and affiliates. 'use client'; /** * @file DropdownMenuItem.tsx * @output Exports DropdownMenuItem component * @position Sub-component; used inside DropdownMenu * * Interactive menu item with role="menuitem". Keyboard navigation * is handled by useListFocus on the parent menu container. * * Composes Item for the shared start content + label + description + end content layout. * Passes role="menuitem" so Item puts onClick on the root div instead of * creating an invisible button (keyboard access is provided by the parent menu). * * SYNC: When modified, update these files to stay in sync: * - /packages/core/src/DropdownMenu/DropdownMenu.doc.mjs * - /packages/core/src/DropdownMenu/DropdownMenuItem.doc.mjs * - /packages/core/src/DropdownMenu/DropdownMenu.test.tsx * - /packages/core/src/DropdownMenu/index.ts * - /apps/storybook/stories/DropdownMenu.stories.tsx * - /packages/cli/assets/templates/blocks/components/DropdownMenu/ (showcase blocks) */ import { useCallback, type PointerEvent, type ReactNode } from 'react'; import * as stylex from "../style-runtime.js"; import { renderIconSlot, type IconType } from "../Icon/index.js"; import { Item } from "../Item/index.js"; import "../theme/tokens.stylex.js"; import { colorVars, spacingVars, typographyVars, typeScaleVars } from "../theme/tokens.stylex.js"; import { mergeProps } from "../utils/index.js"; import type { BaseProps } from "../BaseProps.js"; import { useDropdownMenuContext } from "./DropdownMenuContext.js"; import { focusMenuItemOnHover } from "./menuItemHover.js"; import { themeProps } from "../utils/themeProps.js"; const menuItemStyles = { root: { kB7OPa: "x9f619", kzqmXN: "xh8yej3", k8WAf4: "xce4md1", kg3NbH: "xf314gf", kaIpWk: "x1ws5lxm", kMv6JI: "x9ynric", kGuDYH: "xcr08ib", kMwMTN: "x1tgivj0", kWkggS: "xjbqb8w x1c52tdz x1anq1lc", kkrTdU: "x1ypdohk x16khyan", k9WMMc: "x1yc453h", kI3sdo: "x1a2a7pz", $$css: true as const }, disabled: { kSiTet: "xbyyjgo", kkrTdU: "xt0e3qv", $$css: true as const }, destructive: { kMwMTN: "xjt36v0", "--_item-label-color": "xufyqxy", "--_item-description-color": "xqlix59", $$css: true as const } }; const itemSizeStyles = { sm: { k8WAf4: "xu0wf1k", kg3NbH: "xf314gf", $$css: true as const }, md: { k8WAf4: "x1vofgu7", $$css: true as const }, lg: { $$css: true as const } }; export interface DropdownMenuItemProps extends Pick { /** Icon to display before the label. */ icon?: ReactNode | IconType; /** Primary label text. */ label: ReactNode; /** Secondary description text displayed below the label. */ description?: ReactNode; /** Callback when the item is selected. */ onClick?: () => void; /** Whether the item is disabled. @default false */ isDisabled?: boolean; /** Additional content to render after the label/description. */ endContent?: ReactNode; /** * Whether activating the item closes the menu. Set `false` for an action * that reports its result on the item itself (a copy row swapping to * "Copied"), matching the checkbox and radio items, which already decide * this for themselves. * @default true */ hasCloseOnSelect?: boolean; /** * Visual variant. `'destructive'` renders the label, description, and icon in * the error color for dangerous actions (e.g. Delete). @default 'default' */ variant?: 'default' | 'destructive'; } /** * An interactive dropdown menu item with icon, label, and optional description. * * Must be used inside DropdownMenu. Keyboard navigation is provided * automatically by the parent via useListFocus. * * @example * ``` * * * * * ``` */ export function DropdownMenuItem({ icon, label, description, onClick, isDisabled = false, endContent, hasCloseOnSelect = true, variant = 'default', xstyle, className, style }: DropdownMenuItemProps) { const ctx = useDropdownMenuContext(); const menuSize = ctx?.menuSize ?? 'md'; const handleClick = useCallback(() => { if (isDisabled) { return; } onClick?.(); if (hasCloseOnSelect) { ctx?.closeMenu(); } }, [isDisabled, onClick, hasCloseOnSelect, ctx]); const handlePointerMove = useCallback((e: PointerEvent) => focusMenuItemOnHover(e, isDisabled), [isDisabled]); const isDestructive = variant === 'destructive'; return ; } DropdownMenuItem.displayName = 'DropdownMenuItem';