UNPKG

3.27 kBJavaScriptView Raw
1import _extends from "@babel/runtime/helpers/esm/extends";
2import * as React from 'react';
3import { unstable_useForkRef as useForkRef } from '@mui/utils';
4import useList from '../useList';
5import { useCompoundParent } from '../utils/useCompound';
6import menuReducer from './menuReducer';
7
8/**
9 *
10 * Demos:
11 *
12 * - [Menu](https://mui.com/base/react-menu/#hooks)
13 *
14 * API:
15 *
16 * - [useMenu API](https://mui.com/base/react-menu/hooks-api/#use-menu)
17 */
18export default function useMenu(parameters = {}) {
19 const {
20 defaultOpen,
21 listboxRef: listboxRefProp,
22 open: openProp,
23 onOpenChange
24 } = parameters;
25 const listboxRef = React.useRef(null);
26 const handleRef = useForkRef(listboxRef, listboxRefProp);
27 const {
28 subitems,
29 contextValue: compoundComponentContextValue
30 } = useCompoundParent();
31 const subitemKeys = React.useMemo(() => Array.from(subitems.keys()), [subitems]);
32 const getItemDomElement = React.useCallback(itemId => {
33 if (itemId == null) {
34 return null;
35 }
36 return subitems.get(itemId)?.ref.current ?? null;
37 }, [subitems]);
38 const controlledProps = React.useMemo(() => ({
39 open: openProp
40 }), [openProp]);
41 const stateChangeHandler = React.useCallback((event, field, fieldValue, reason, state) => {
42 if (field === 'open') {
43 onOpenChange?.(fieldValue);
44 if (fieldValue === true && state.highlightedValue !== null) {
45 subitems.get(state.highlightedValue)?.ref.current?.focus();
46 }
47 }
48 }, [onOpenChange, subitems]);
49 const {
50 dispatch,
51 getRootProps,
52 contextValue: listContextValue,
53 state: {
54 open,
55 highlightedValue
56 },
57 rootRef: mergedListRef
58 } = useList({
59 controlledProps,
60 disabledItemsFocusable: true,
61 focusManagement: 'DOM',
62 getItemDomElement,
63 getInitialState: () => ({
64 selectedValues: [],
65 highlightedValue: null,
66 open: defaultOpen ?? false
67 }),
68 isItemDisabled: id => subitems?.get(id)?.disabled || false,
69 items: subitemKeys,
70 getItemAsString: id => subitems.get(id)?.label || subitems.get(id)?.ref.current?.innerText,
71 rootRef: handleRef,
72 onStateChange: stateChangeHandler,
73 reducerActionContext: {
74 listboxRef
75 },
76 selectionMode: 'none',
77 stateReducer: menuReducer
78 });
79 React.useEffect(() => {
80 if (open && highlightedValue === subitemKeys[0]) {
81 subitems.get(subitemKeys[0])?.ref?.current?.focus();
82 }
83 }, [open, highlightedValue, subitems, subitemKeys]);
84 React.useEffect(() => {
85 // set focus to the highlighted item (but prevent stealing focus from other elements on the page)
86 if (listboxRef.current?.contains(document.activeElement) && highlightedValue !== null) {
87 subitems?.get(highlightedValue)?.ref.current?.focus();
88 }
89 }, [highlightedValue, subitems]);
90 const getListboxProps = (otherHandlers = {}) => {
91 const rootProps = getRootProps(otherHandlers);
92 return _extends({}, otherHandlers, rootProps, {
93 role: 'menu'
94 });
95 };
96 React.useDebugValue({
97 subitems,
98 highlightedValue
99 });
100 return {
101 contextValue: _extends({}, compoundComponentContextValue, listContextValue),
102 dispatch,
103 getListboxProps,
104 highlightedValue,
105 listboxRef: mergedListRef,
106 menuItems: subitems,
107 open
108 };
109}
\No newline at end of file