import React, { forwardRef, RefAttributes, useContext, type JSX } from "react"; import classNames from "classnames"; import { useTreeState } from "react-stately"; import { Node, SectionProps } from "@react-types/shared"; import { AriaMenuProps, useMenu, useMenuItem, useMenuSection, } from "@react-aria/menu"; import { filterDOMProps, mergeProps } from "@react-aria/utils"; import { BaseCollection, Collection, CollectionBuilder, createBranchComponent, createLeafComponent, } from "@react-aria/collections"; import { RenderBaseProps, RenderStyleProps } from "../../../types"; import { Divider } from "@components/Content/Divider"; import { useForwardedRef } from "@hooks/useForwardedRef"; import { Provider } from "@components/Internal/Provider"; import { useCollectionRenderer } from "@hooks/useCollectionRenderer"; import { useRenderProps } from "@hooks"; import { useFocusRing } from "@hooks/useFocusRing"; import { useContextProps } from "@hooks/useContextProps"; import { ItemContext, ItemProps, SectionContext } from "@components/Collection"; import { MenuContext, MenuStateContext } from "./Menu.context"; import { MenuList, MenuOption, MenuSectionTitle, MenuSectionList, } from "./Menu.styles"; export interface MenuProps extends AriaMenuProps, Omit, "children"> {} type ForwardedMenu = { (props: MenuProps & RefAttributes): JSX.Element; displayName: string; /** A section in a Menu * @deprecated Use `Section` instead */ Section: typeof MenuSectionWrapper; /** An item in a Menu * @deprecated Use `Item` instead */ Item: typeof MenuItemWrapper; }; /** A Menu is a collection of items that the user can select. * When an item in the menu is selected, an associated action is performed */ export const Menu = forwardRef(function Menu( props: MenuProps, ref: React.Ref ) { [props, ref] = useContextProps(MenuContext, props, ref); return ( }> {(collection: BaseCollection) => collection.size > 0 && ( {...props} collection={collection} menuRef={ref} /> ) } ); }) as unknown as ForwardedMenu; Menu.displayName = "Menu"; interface MenuInnerProps extends MenuProps { collection: BaseCollection; menuRef: React.ForwardedRef; } function MenuInner(props: MenuInnerProps) { const { menuRef, className } = props; const state = useTreeState(props); const ref = useForwardedRef(menuRef); const { menuProps } = useMenu(props, state, ref); const { CollectionRenderer } = useCollectionRenderer(); return ( ); } export interface MenuSectionProps extends SectionProps, RenderStyleProps {} function MenuSection( props: MenuSectionProps, ref: React.ForwardedRef, section: Node ) { const state = useContext(MenuStateContext)!; const { CollectionBranchRenderer } = useCollectionRenderer(); const { itemProps, headingProps, groupProps } = useMenuSection({ heading: props.title, "aria-label": section["aria-label"], }); const renderProps = useRenderProps({ componentClassName: "aje-menu__section", ...props, children: props.title, }); // If the section is not the first, add a separator element. // The heading is rendered inside an
  • element, which contains // a
      with the child items. return ( <> {section.key !== state.collection.getFirstKey() && }
    • {renderProps.children && ( {renderProps.children} )}
    • ); } const MenuSectionWrapper = createBranchComponent("section", MenuSection); // @ts-expect-error MenuSectionWrapper.displayName = "Menu.Section"; Menu.Section = MenuSectionWrapper; const MenuItem = ( props: ItemProps, ref: React.ForwardedRef, item: Node ) => { const state = useContext(MenuStateContext)!; const internalRef = useForwardedRef(ref); const { menuItemProps, isSelected } = useMenuItem( { key: item.key, "aria-label": props["aria-label"] }, state, internalRef ); const { focusProps, isFocused, isFocusVisible } = useFocusRing(); const renderProps = useRenderProps({ componentClassName: "aje-menu__item", ...props, children: item.rendered, values: { isSelected, isFocused, isFocusVisible, }, }); return ( {renderProps.children} ); }; const MenuItemWrapper = createLeafComponent("item", MenuItem); // @ts-expect-error MenuItemWrapper.displayName = "Menu.Item"; Menu.Item = MenuItemWrapper;