@itwin/itwinui-react
Version:
A react component library for iTwinUI
106 lines (105 loc) • 4.5 kB
TypeScript
import * as React from 'react';
import type { PolymorphicForwardRefComponent, PortalProps } from '../../utils/index.js';
import { usePopover } from '../Popover/Popover.js';
import { useInteractions, type UseListNavigationProps } from '@floating-ui/react';
type UsePopoverProps = Parameters<typeof usePopover>[0];
type MenuProps = {
/**
* Menu items. Recommended to use `MenuItem` components.
*
* If you have custom actionable items, they should have `tabIndex={-1}` for better keyboard navigation support
* and selected item should have `aria-selected={true}`.
*/
children: React.ReactNode;
/**
* The trigger that opens the menu.
*/
trigger: React.ReactNode;
/**
* You can use this optional prop when the position reference is not the trigger.
*
* This can be either a real element, or a [virtual element](https://floating-ui.com/docs/virtual-elements)
* containing a `getBoundingClientRect` method.
*/
positionReference?: Parameters<ReturnType<typeof usePopover>['refs']['setPositionReference']>[0];
/**
* Use this prop to override the default props passed to `usePopover` and `useListNavigation`.
*/
popoverProps?: Omit<UsePopoverProps, 'interactions'> & {
interactions?: UsePopoverProps['interactions'] & {
listNavigation?: Partial<UseListNavigationProps>;
};
};
/**
* If not passed, uses the `portal` from its parent `Menu`.
* @see {@link PortalProps.portal} for docs on the prop.
*/
portal?: PortalProps['portal'];
};
/**
* @private
*
* Used for dropdown components. E.g. `DropdownMenu`, `SplitButton`.
*
* What needs to be handled **manually**:
* - Menu items need to spread `MenuContext.popover.getItemProps()`.
* - Menu items need to focus itself on hover.
*
* What is handled automatically:
* - the portaling: use the optional `portal` prop for more customization
* - conditional rendering based on the popover's open state
* - spreading the popover props `getFloatingProps` and `getReferenceProps`.
* - As mentioned above, `getItemProps` need to be spread **manually** in the menu item.
* - setting the refs: use the optional`positionReference` prop to set the position reference
* - keyboard navigation: use the `interactions.listNavigation` prop for more customization
* - registering a `FloatingNode` in the `FloatingTree` if an ancestral `FloatingTree` is found
* - focus management:
* - focuses items on hover.
* - if *not* in a `FloatingTree`, focus moves to the trigger when the menu is closed.
* If in a `FloatingTree`, focus does not move back to the trigger since menu items handle the focus.
* - setting `aria-expanded` accordingly depending on the menu open state
*
* All `Menu` popover interactions are identical to `usePopover`'s interactions. Exception:
* - `hover`: When the `Menu` is within a `FloatingTree`, if a submenu has focus, the hover interaction is automatically
* disabled. This helps to keep the last hovered/focused submenu open even upon hovering out.
*
* @example
* const trigger = <Button>Menu</Button>;
* const [positionReference, setPositionReference] = React.useState<HTMLDivElement | null>(null);
* const popoverProps = { matchWidth: true };
* const nodeId = useFloatingNodeId();
*
* return (
* <Box ref={setPositionReference}>
* <Menu
* trigger={trigger}
* positionReference={positionReference}
* nodeId={nodeId}
* popoverProps={popoverProps}
* >
* <MenuItem>Item 1</MenuItem>
* <MenuItem>Item 2</MenuItem>
* </Menu>
* </Box>
* );
*/
export declare const Menu: PolymorphicForwardRefComponent<"div", MenuProps>;
export type TreeEvent = {
nodeId: string;
parentId: string | null;
};
type PopoverGetItemProps = ({ focusableItemIndex, userProps, }: {
/**
* Index of this item out of all the focusable items in the parent `Menu`
*/
focusableItemIndex: number | undefined;
userProps?: Parameters<NonNullable<ReturnType<typeof useInteractions>['getItemProps']>>[0];
}) => ReturnType<ReturnType<typeof useInteractions>['getItemProps']>;
export declare const MenuContext: React.Context<{
popoverGetItemProps: PopoverGetItemProps;
focusableElements: HTMLElement[];
} | undefined>;
export declare const MenuPortalContext: React.Context<boolean | {
to: HTMLElement | null | undefined | (() => HTMLElement | null | undefined);
} | undefined>;
export {};