| 79 | * @param state - State for the menu, as returned by `useListState`. |
| 80 | */ |
| 81 | export function useMenu<T>( |
| 82 | props: AriaMenuOptions<T>, |
| 83 | state: TreeState<T>, |
| 84 | ref: RefObject<HTMLElement | null> |
| 85 | ): MenuAria { |
| 86 | let {shouldFocusWrap = true, onKeyDown, onKeyUp, ...otherProps} = props; |
| 87 | |
| 88 | if (!props['aria-label'] && !props['aria-labelledby'] && process.env.NODE_ENV !== 'production') { |
| 89 | console.warn('An aria-label or aria-labelledby prop is required for accessibility.'); |
| 90 | } |
| 91 | |
| 92 | let domProps = filterDOMProps(props, {labelable: true}); |
| 93 | let {listProps} = useSelectableList({ |
| 94 | ...otherProps, |
| 95 | ref, |
| 96 | selectionManager: state.selectionManager, |
| 97 | collection: state.collection, |
| 98 | disabledKeys: state.disabledKeys, |
| 99 | shouldFocusWrap, |
| 100 | linkBehavior: 'override' |
| 101 | }); |
| 102 | |
| 103 | menuData.set(state, { |
| 104 | onClose: props.onClose, |
| 105 | onAction: props.onAction, |
| 106 | shouldUseVirtualFocus: props.shouldUseVirtualFocus |
| 107 | }); |
| 108 | |
| 109 | return { |
| 110 | menuProps: mergeProps( |
| 111 | domProps, |
| 112 | {onKeyDown, onKeyUp}, |
| 113 | { |
| 114 | role: 'menu', |
| 115 | ...listProps, |
| 116 | onKeyDown: e => { |
| 117 | // don't clear the menu selected keys if the user is presses escape since escape closes the menu |
| 118 | if (e.key !== 'Escape' || props.shouldUseVirtualFocus) { |
| 119 | listProps.onKeyDown?.(e); |
| 120 | } |
| 121 | } |
| 122 | } |
| 123 | ) |
| 124 | }; |
| 125 | } |