@react-aria/menu 3.15.4 → 3.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/dist/types.d.ts +5 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/useMenu.main.js +2 -1
- package/dist/useMenu.main.js.map +1 -1
- package/dist/useMenu.mjs +2 -1
- package/dist/useMenu.module.js +2 -1
- package/dist/useMenu.module.js.map +1 -1
- package/dist/useMenuItem.main.js +19 -16
- package/dist/useMenuItem.main.js.map +1 -1
- package/dist/useMenuItem.mjs +19 -16
- package/dist/useMenuItem.module.js +19 -16
- package/dist/useMenuItem.module.js.map +1 -1
- package/dist/useMenuTrigger.main.js +1 -0
- package/dist/useMenuTrigger.main.js.map +1 -1
- package/dist/useMenuTrigger.mjs +1 -0
- package/dist/useMenuTrigger.module.js +1 -0
- package/dist/useMenuTrigger.module.js.map +1 -1
- package/dist/useSafelyMouseToSubmenu.main.js +1 -1
- package/dist/useSafelyMouseToSubmenu.main.js.map +1 -1
- package/dist/useSafelyMouseToSubmenu.mjs +1 -1
- package/dist/useSafelyMouseToSubmenu.module.js +1 -1
- package/dist/useSafelyMouseToSubmenu.module.js.map +1 -1
- package/dist/useSubmenuTrigger.main.js +8 -4
- package/dist/useSubmenuTrigger.main.js.map +1 -1
- package/dist/useSubmenuTrigger.mjs +8 -4
- package/dist/useSubmenuTrigger.module.js +8 -4
- package/dist/useSubmenuTrigger.module.js.map +1 -1
- package/package.json +19 -17
- package/src/useMenu.ts +1 -1
- package/src/useMenuItem.ts +27 -19
- package/src/useMenuTrigger.ts +2 -1
- package/src/useSafelyMouseToSubmenu.ts +1 -1
- package/src/useSubmenuTrigger.ts +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"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","sources":["packages/@react-aria/menu/src/useSubmenuTrigger.ts"],"sourcesContent":["/*\n * Copyright 2023 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {AriaMenuItemProps} from './useMenuItem';\nimport {AriaMenuOptions} from './useMenu';\nimport type {AriaPopoverProps, OverlayProps} from '@react-aria/overlays';\nimport {FocusableElement, FocusStrategy, KeyboardEvent, Node, PressEvent, RefObject} from '@react-types/shared';\nimport type {SubmenuTriggerState} from '@react-stately/menu';\nimport {useCallback, useRef} from 'react';\nimport {useEffectEvent, useId, useLayoutEffect} from '@react-aria/utils';\nimport {useLocale} from '@react-aria/i18n';\nimport {useSafelyMouseToSubmenu} from './useSafelyMouseToSubmenu';\n\nexport interface AriaSubmenuTriggerProps {\n /**\n * An object representing the submenu trigger menu item. Contains all the relevant information that makes up the menu item.\n * @deprecated\n */\n node?: Node<unknown>,\n /** Whether the submenu trigger is disabled. */\n isDisabled?: boolean,\n /** The type of the contents that the submenu trigger opens. */\n type?: 'dialog' | 'menu',\n /** Ref of the menu that contains the submenu trigger. */\n parentMenuRef: RefObject<HTMLElement | null>,\n /** Ref of the submenu opened by the submenu trigger. */\n submenuRef: RefObject<HTMLElement | null>,\n /**\n * The delay time in milliseconds for the submenu to appear after hovering over the trigger.\n * @default 200\n */\n delay?: number\n}\n\ninterface SubmenuTriggerProps extends AriaMenuItemProps {\n /** Whether the submenu trigger is in an expanded state. */\n isOpen: boolean\n}\n\ninterface SubmenuProps<T> extends AriaMenuOptions<T> {\n /** The level of the submenu. */\n submenuLevel: number\n}\n\nexport interface SubmenuTriggerAria<T> {\n /** Props for the submenu trigger menu item. */\n submenuTriggerProps: SubmenuTriggerProps,\n /** Props for the submenu controlled by the submenu trigger menu item. */\n submenuProps: SubmenuProps<T>,\n /** Props for the submenu's popover container. */\n popoverProps: Pick<AriaPopoverProps, 'isNonModal' | 'shouldCloseOnInteractOutside'> & Pick<OverlayProps, 'disableFocusManagement'>\n}\n\n/**\n * Provides the behavior and accessibility implementation for a submenu trigger and its associated submenu.\n * @param props - Props for the submenu trigger and refs attach to its submenu and parent menu.\n * @param state - State for the submenu trigger.\n * @param ref - Ref to the submenu trigger element.\n */\nexport function useSubmenuTrigger<T>(props: AriaSubmenuTriggerProps, state: SubmenuTriggerState, ref: RefObject<FocusableElement | null>): SubmenuTriggerAria<T> {\n let {parentMenuRef, submenuRef, type = 'menu', isDisabled, delay = 200} = props;\n let submenuTriggerId = useId();\n let overlayId = useId();\n let {direction} = useLocale();\n let openTimeout = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n let cancelOpenTimeout = useCallback(() => {\n if (openTimeout.current) {\n clearTimeout(openTimeout.current);\n openTimeout.current = undefined;\n }\n }, [openTimeout]);\n\n let onSubmenuOpen = useEffectEvent((focusStrategy?: FocusStrategy) => {\n cancelOpenTimeout();\n state.open(focusStrategy);\n });\n\n let onSubmenuClose = useEffectEvent(() => {\n cancelOpenTimeout();\n state.close();\n });\n\n useLayoutEffect(() => {\n return () => {\n cancelOpenTimeout();\n };\n }, [cancelOpenTimeout]);\n\n let submenuKeyDown = (e: KeyboardEvent) => {\n switch (e.key) {\n case 'ArrowLeft':\n if (direction === 'ltr' && e.currentTarget.contains(e.target as Element)) {\n e.stopPropagation();\n onSubmenuClose();\n ref.current.focus();\n }\n break;\n case 'ArrowRight':\n if (direction === 'rtl' && e.currentTarget.contains(e.target as Element)) {\n e.stopPropagation();\n onSubmenuClose();\n ref.current.focus();\n }\n break;\n case 'Escape':\n e.stopPropagation();\n state.closeAll();\n break;\n }\n };\n\n let submenuProps = {\n id: overlayId,\n 'aria-labelledby': submenuTriggerId,\n submenuLevel: state.submenuLevel,\n ...(type === 'menu' && {\n onClose: state.closeAll,\n autoFocus: state.focusStrategy,\n onKeyDown: submenuKeyDown\n })\n };\n\n let submenuTriggerKeyDown = (e: KeyboardEvent) => {\n switch (e.key) {\n case 'ArrowRight':\n if (!isDisabled) {\n if (direction === 'ltr') {\n if (!state.isOpen) {\n onSubmenuOpen('first');\n }\n\n if (type === 'menu' && !!submenuRef?.current && document.activeElement === ref?.current) {\n submenuRef.current.focus();\n }\n } else if (state.isOpen) {\n onSubmenuClose();\n } else {\n e.continuePropagation();\n }\n }\n\n break;\n case 'ArrowLeft':\n if (!isDisabled) {\n if (direction === 'rtl') {\n if (!state.isOpen) {\n onSubmenuOpen('first');\n }\n\n if (type === 'menu' && !!submenuRef?.current && document.activeElement === ref?.current) {\n submenuRef.current.focus();\n }\n } else if (state.isOpen) {\n onSubmenuClose();\n } else {\n e.continuePropagation();\n }\n }\n break;\n case 'Escape':\n state.closeAll();\n break;\n default:\n e.continuePropagation();\n break;\n }\n };\n\n let onPressStart = (e: PressEvent) => {\n if (!isDisabled && (e.pointerType === 'virtual' || e.pointerType === 'keyboard')) {\n // If opened with a screen reader or keyboard, auto focus the first submenu item.\n onSubmenuOpen('first');\n }\n };\n\n let onPress = (e: PressEvent) => {\n if (!isDisabled && (e.pointerType === 'touch' || e.pointerType === 'mouse')) {\n // For touch or on a desktop device with a small screen open on press up to possible problems with\n // press up happening on the newly opened tray items\n onSubmenuOpen();\n }\n };\n\n let onHoverChange = (isHovered) => {\n if (!isDisabled) {\n if (isHovered && !state.isOpen) {\n if (!openTimeout.current) {\n openTimeout.current = setTimeout(() => {\n onSubmenuOpen();\n }, delay);\n }\n } else if (!isHovered) {\n cancelOpenTimeout();\n }\n }\n };\n\n let onBlur = (e) => {\n if (state.isOpen && parentMenuRef.current.contains(e.relatedTarget)) {\n onSubmenuClose();\n }\n };\n\n let shouldCloseOnInteractOutside = (target) => {\n if (target !== ref.current) {\n return true;\n }\n\n return false;\n };\n\n useSafelyMouseToSubmenu({menuRef: parentMenuRef, submenuRef, isOpen: state.isOpen, isDisabled: isDisabled});\n\n return {\n submenuTriggerProps: {\n id: submenuTriggerId,\n 'aria-controls': state.isOpen ? overlayId : undefined,\n 'aria-haspopup': !isDisabled ? type : undefined,\n 'aria-expanded': state.isOpen ? 'true' : 'false',\n onPressStart,\n onPress,\n onHoverChange,\n onKeyDown: submenuTriggerKeyDown,\n onBlur,\n isOpen: state.isOpen\n },\n submenuProps,\n popoverProps: {\n isNonModal: true,\n disableFocusManagement: true,\n shouldCloseOnInteractOutside\n }\n };\n}\n"],"names":[],"version":3,"file":"useSubmenuTrigger.module.js.map"}
|
|
1
|
+
{"mappings":"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","sources":["packages/@react-aria/menu/src/useSubmenuTrigger.ts"],"sourcesContent":["/*\n * Copyright 2023 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {AriaMenuItemProps} from './useMenuItem';\nimport {AriaMenuOptions} from './useMenu';\nimport type {AriaPopoverProps, OverlayProps} from '@react-aria/overlays';\nimport {FocusableElement, FocusStrategy, KeyboardEvent, Node, PressEvent, RefObject} from '@react-types/shared';\nimport type {SubmenuTriggerState} from '@react-stately/menu';\nimport {useCallback, useRef} from 'react';\nimport {useEffectEvent, useId, useLayoutEffect} from '@react-aria/utils';\nimport {useLocale} from '@react-aria/i18n';\nimport {useSafelyMouseToSubmenu} from './useSafelyMouseToSubmenu';\n\nexport interface AriaSubmenuTriggerProps {\n /**\n * An object representing the submenu trigger menu item. Contains all the relevant information that makes up the menu item.\n * @deprecated\n */\n node?: Node<unknown>,\n /** Whether the submenu trigger is disabled. */\n isDisabled?: boolean,\n /** The type of the contents that the submenu trigger opens. */\n type?: 'dialog' | 'menu',\n /** Ref of the menu that contains the submenu trigger. */\n parentMenuRef: RefObject<HTMLElement | null>,\n /** Ref of the submenu opened by the submenu trigger. */\n submenuRef: RefObject<HTMLElement | null>,\n /**\n * The delay time in milliseconds for the submenu to appear after hovering over the trigger.\n * @default 200\n */\n delay?: number\n}\n\ninterface SubmenuTriggerProps extends Omit<AriaMenuItemProps, 'key'> {\n /** Whether the submenu trigger is in an expanded state. */\n isOpen: boolean\n}\n\ninterface SubmenuProps<T> extends AriaMenuOptions<T> {\n /** The level of the submenu. */\n submenuLevel: number\n}\n\nexport interface SubmenuTriggerAria<T> {\n /** Props for the submenu trigger menu item. */\n submenuTriggerProps: SubmenuTriggerProps,\n /** Props for the submenu controlled by the submenu trigger menu item. */\n submenuProps: SubmenuProps<T>,\n /** Props for the submenu's popover container. */\n popoverProps: Pick<AriaPopoverProps, 'isNonModal' | 'shouldCloseOnInteractOutside'> & Pick<OverlayProps, 'disableFocusManagement'>\n}\n\n/**\n * Provides the behavior and accessibility implementation for a submenu trigger and its associated submenu.\n * @param props - Props for the submenu trigger and refs attach to its submenu and parent menu.\n * @param state - State for the submenu trigger.\n * @param ref - Ref to the submenu trigger element.\n */\nexport function useSubmenuTrigger<T>(props: AriaSubmenuTriggerProps, state: SubmenuTriggerState, ref: RefObject<FocusableElement | null>): SubmenuTriggerAria<T> {\n let {parentMenuRef, submenuRef, type = 'menu', isDisabled, delay = 200} = props;\n let submenuTriggerId = useId();\n let overlayId = useId();\n let {direction} = useLocale();\n let openTimeout = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n let cancelOpenTimeout = useCallback(() => {\n if (openTimeout.current) {\n clearTimeout(openTimeout.current);\n openTimeout.current = undefined;\n }\n }, [openTimeout]);\n\n let onSubmenuOpen = useEffectEvent((focusStrategy?: FocusStrategy) => {\n cancelOpenTimeout();\n state.open(focusStrategy);\n });\n\n let onSubmenuClose = useEffectEvent(() => {\n cancelOpenTimeout();\n state.close();\n });\n\n useLayoutEffect(() => {\n return () => {\n cancelOpenTimeout();\n };\n }, [cancelOpenTimeout]);\n\n let submenuKeyDown = (e: KeyboardEvent) => {\n switch (e.key) {\n case 'ArrowLeft':\n if (direction === 'ltr' && e.currentTarget.contains(e.target as Element)) {\n e.stopPropagation();\n onSubmenuClose();\n ref.current?.focus();\n }\n break;\n case 'ArrowRight':\n if (direction === 'rtl' && e.currentTarget.contains(e.target as Element)) {\n e.stopPropagation();\n onSubmenuClose();\n ref.current?.focus();\n }\n break;\n case 'Escape':\n e.stopPropagation();\n state.closeAll();\n break;\n }\n };\n\n let submenuProps = {\n id: overlayId,\n 'aria-labelledby': submenuTriggerId,\n submenuLevel: state.submenuLevel,\n ...(type === 'menu' && {\n onClose: state.closeAll,\n autoFocus: state.focusStrategy ?? undefined,\n onKeyDown: submenuKeyDown\n })\n };\n\n let submenuTriggerKeyDown = (e: KeyboardEvent) => {\n switch (e.key) {\n case 'ArrowRight':\n if (!isDisabled) {\n if (direction === 'ltr') {\n if (!state.isOpen) {\n onSubmenuOpen('first');\n }\n\n if (type === 'menu' && !!submenuRef?.current && document.activeElement === ref?.current) {\n submenuRef.current.focus();\n }\n } else if (state.isOpen) {\n onSubmenuClose();\n } else {\n e.continuePropagation();\n }\n }\n\n break;\n case 'ArrowLeft':\n if (!isDisabled) {\n if (direction === 'rtl') {\n if (!state.isOpen) {\n onSubmenuOpen('first');\n }\n\n if (type === 'menu' && !!submenuRef?.current && document.activeElement === ref?.current) {\n submenuRef.current.focus();\n }\n } else if (state.isOpen) {\n onSubmenuClose();\n } else {\n e.continuePropagation();\n }\n }\n break;\n case 'Escape':\n state.closeAll();\n break;\n default:\n e.continuePropagation();\n break;\n }\n };\n\n let onPressStart = (e: PressEvent) => {\n if (!isDisabled && (e.pointerType === 'virtual' || e.pointerType === 'keyboard')) {\n // If opened with a screen reader or keyboard, auto focus the first submenu item.\n onSubmenuOpen('first');\n }\n };\n\n let onPress = (e: PressEvent) => {\n if (!isDisabled && (e.pointerType === 'touch' || e.pointerType === 'mouse')) {\n // For touch or on a desktop device with a small screen open on press up to possible problems with\n // press up happening on the newly opened tray items\n onSubmenuOpen();\n }\n };\n\n let onHoverChange = (isHovered) => {\n if (!isDisabled) {\n if (isHovered && !state.isOpen) {\n if (!openTimeout.current) {\n openTimeout.current = setTimeout(() => {\n onSubmenuOpen();\n }, delay);\n }\n } else if (!isHovered) {\n cancelOpenTimeout();\n }\n }\n };\n\n let onBlur = (e) => {\n if (state.isOpen && parentMenuRef.current?.contains(e.relatedTarget)) {\n onSubmenuClose();\n }\n };\n\n let shouldCloseOnInteractOutside = (target) => {\n if (target !== ref.current) {\n return true;\n }\n\n return false;\n };\n\n useSafelyMouseToSubmenu({menuRef: parentMenuRef, submenuRef, isOpen: state.isOpen, isDisabled: isDisabled});\n\n return {\n submenuTriggerProps: {\n id: submenuTriggerId,\n 'aria-controls': state.isOpen ? overlayId : undefined,\n 'aria-haspopup': !isDisabled ? type : undefined,\n 'aria-expanded': state.isOpen ? 'true' : 'false',\n onPressStart,\n onPress,\n onHoverChange,\n onKeyDown: submenuTriggerKeyDown,\n onBlur,\n isOpen: state.isOpen\n },\n submenuProps,\n popoverProps: {\n isNonModal: true,\n disableFocusManagement: true,\n shouldCloseOnInteractOutside\n }\n };\n}\n"],"names":[],"version":3,"file":"useSubmenuTrigger.module.js.map"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-aria/menu",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.16.0",
|
|
4
4
|
"description": "Spectrum UI components in React",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "dist/main.js",
|
|
@@ -22,25 +22,27 @@
|
|
|
22
22
|
"url": "https://github.com/adobe/react-spectrum"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@react-aria/focus": "^3.
|
|
26
|
-
"@react-aria/i18n": "^3.12.
|
|
27
|
-
"@react-aria/interactions": "^3.22.
|
|
28
|
-
"@react-aria/overlays": "^3.
|
|
29
|
-
"@react-aria/selection": "^3.
|
|
30
|
-
"@react-aria/utils": "^3.
|
|
31
|
-
"@react-stately/collections": "^3.
|
|
32
|
-
"@react-stately/menu": "^3.
|
|
33
|
-
"@react-stately/
|
|
34
|
-
"@react-
|
|
35
|
-
"@react-types/
|
|
36
|
-
"@react-types/
|
|
25
|
+
"@react-aria/focus": "^3.19.0",
|
|
26
|
+
"@react-aria/i18n": "^3.12.4",
|
|
27
|
+
"@react-aria/interactions": "^3.22.5",
|
|
28
|
+
"@react-aria/overlays": "^3.24.0",
|
|
29
|
+
"@react-aria/selection": "^3.21.0",
|
|
30
|
+
"@react-aria/utils": "^3.26.0",
|
|
31
|
+
"@react-stately/collections": "^3.12.0",
|
|
32
|
+
"@react-stately/menu": "^3.9.0",
|
|
33
|
+
"@react-stately/selection": "^3.18.0",
|
|
34
|
+
"@react-stately/tree": "^3.8.6",
|
|
35
|
+
"@react-types/button": "^3.10.1",
|
|
36
|
+
"@react-types/menu": "^3.9.13",
|
|
37
|
+
"@react-types/shared": "^3.26.0",
|
|
37
38
|
"@swc/helpers": "^0.5.0"
|
|
38
39
|
},
|
|
39
40
|
"peerDependencies": {
|
|
40
|
-
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0",
|
|
41
|
-
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0"
|
|
41
|
+
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
|
|
42
|
+
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
|
42
43
|
},
|
|
43
44
|
"publishConfig": {
|
|
44
45
|
"access": "public"
|
|
45
|
-
}
|
|
46
|
-
|
|
46
|
+
},
|
|
47
|
+
"gitHead": "71f0ef23053f9e03ee7e97df736e8b083e006849"
|
|
48
|
+
}
|
package/src/useMenu.ts
CHANGED
|
@@ -80,7 +80,7 @@ export function useMenu<T>(props: AriaMenuOptions<T>, state: TreeState<T>, ref:
|
|
|
80
80
|
onKeyDown: (e) => {
|
|
81
81
|
// don't clear the menu selected keys if the user is presses escape since escape closes the menu
|
|
82
82
|
if (e.key !== 'Escape') {
|
|
83
|
-
listProps.onKeyDown(e);
|
|
83
|
+
listProps.onKeyDown?.(e);
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
})
|
package/src/useMenuItem.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {filterDOMProps, mergeProps, useLinkProps, useRouter, useSlotId} from '@r
|
|
|
15
15
|
import {getItemCount} from '@react-stately/collections';
|
|
16
16
|
import {isFocusVisible, useFocus, useHover, useKeyboard, usePress} from '@react-aria/interactions';
|
|
17
17
|
import {menuData} from './useMenu';
|
|
18
|
+
import {SelectionManager} from '@react-stately/selection';
|
|
18
19
|
import {TreeState} from '@react-stately/tree';
|
|
19
20
|
import {useSelectableItem} from '@react-aria/selection';
|
|
20
21
|
|
|
@@ -58,7 +59,7 @@ export interface AriaMenuItemProps extends DOMProps, PressEvents, HoverEvents, K
|
|
|
58
59
|
'aria-label'?: string,
|
|
59
60
|
|
|
60
61
|
/** The unique key for the menu item. */
|
|
61
|
-
key
|
|
62
|
+
key: Key,
|
|
62
63
|
|
|
63
64
|
/**
|
|
64
65
|
* Handler that is called when the menu should close after selecting an item.
|
|
@@ -88,7 +89,10 @@ export interface AriaMenuItemProps extends DOMProps, PressEvents, HoverEvents, K
|
|
|
88
89
|
'aria-expanded'?: boolean | 'true' | 'false',
|
|
89
90
|
|
|
90
91
|
/** Identifies the menu item's popup element whose contents or presence is controlled by the menu item. */
|
|
91
|
-
'aria-controls'?: string
|
|
92
|
+
'aria-controls'?: string,
|
|
93
|
+
|
|
94
|
+
/** Override of the selection manager. By default, `state.selectionManager` is used. */
|
|
95
|
+
selectionManager?: SelectionManager
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
/**
|
|
@@ -116,13 +120,15 @@ export function useMenuItem<T>(props: AriaMenuItemProps, state: TreeState<T>, re
|
|
|
116
120
|
onKeyUp,
|
|
117
121
|
onFocus,
|
|
118
122
|
onFocusChange,
|
|
119
|
-
onBlur
|
|
123
|
+
onBlur,
|
|
124
|
+
selectionManager = state.selectionManager
|
|
120
125
|
} = props;
|
|
121
126
|
|
|
122
127
|
let isTrigger = !!hasPopup;
|
|
123
|
-
let
|
|
124
|
-
let
|
|
125
|
-
let
|
|
128
|
+
let isTriggerExpanded = isTrigger && props['aria-expanded'] === 'true';
|
|
129
|
+
let isDisabled = props.isDisabled ?? selectionManager.isDisabled(key);
|
|
130
|
+
let isSelected = props.isSelected ?? selectionManager.isSelected(key);
|
|
131
|
+
let data = menuData.get(state)!;
|
|
126
132
|
let item = state.collection.getItem(key);
|
|
127
133
|
let onClose = props.onClose || data.onClose;
|
|
128
134
|
let router = useRouter();
|
|
@@ -143,16 +149,16 @@ export function useMenuItem<T>(props: AriaMenuItemProps, state: TreeState<T>, re
|
|
|
143
149
|
onAction(key);
|
|
144
150
|
}
|
|
145
151
|
|
|
146
|
-
if (e.target instanceof HTMLAnchorElement) {
|
|
152
|
+
if (e.target instanceof HTMLAnchorElement && item) {
|
|
147
153
|
router.open(e.target, e, item.props.href, item.props.routerOptions as RouterOptions);
|
|
148
154
|
}
|
|
149
155
|
};
|
|
150
156
|
|
|
151
157
|
let role = 'menuitem';
|
|
152
158
|
if (!isTrigger) {
|
|
153
|
-
if (
|
|
159
|
+
if (selectionManager.selectionMode === 'single') {
|
|
154
160
|
role = 'menuitemradio';
|
|
155
|
-
} else if (
|
|
161
|
+
} else if (selectionManager.selectionMode === 'multiple') {
|
|
156
162
|
role = 'menuitemcheckbox';
|
|
157
163
|
}
|
|
158
164
|
}
|
|
@@ -173,7 +179,7 @@ export function useMenuItem<T>(props: AriaMenuItemProps, state: TreeState<T>, re
|
|
|
173
179
|
'aria-expanded': props['aria-expanded']
|
|
174
180
|
};
|
|
175
181
|
|
|
176
|
-
if (
|
|
182
|
+
if (selectionManager.selectionMode !== 'none' && !isTrigger) {
|
|
177
183
|
ariaProps['aria-checked'] = isSelected;
|
|
178
184
|
}
|
|
179
185
|
|
|
@@ -196,7 +202,7 @@ export function useMenuItem<T>(props: AriaMenuItemProps, state: TreeState<T>, re
|
|
|
196
202
|
|
|
197
203
|
// Pressing a menu item should close by default in single selection mode but not multiple
|
|
198
204
|
// selection mode, except if overridden by the closeOnSelect prop.
|
|
199
|
-
if (!isTrigger && onClose && (closeOnSelect ?? (
|
|
205
|
+
if (!isTrigger && onClose && (closeOnSelect ?? (selectionManager.selectionMode !== 'multiple' || selectionManager.isLink(key)))) {
|
|
200
206
|
onClose();
|
|
201
207
|
}
|
|
202
208
|
}
|
|
@@ -205,7 +211,7 @@ export function useMenuItem<T>(props: AriaMenuItemProps, state: TreeState<T>, re
|
|
|
205
211
|
};
|
|
206
212
|
|
|
207
213
|
let {itemProps, isFocused} = useSelectableItem({
|
|
208
|
-
selectionManager:
|
|
214
|
+
selectionManager: selectionManager,
|
|
209
215
|
key,
|
|
210
216
|
ref,
|
|
211
217
|
shouldSelectOnPressUp: true,
|
|
@@ -228,9 +234,10 @@ export function useMenuItem<T>(props: AriaMenuItemProps, state: TreeState<T>, re
|
|
|
228
234
|
let {hoverProps} = useHover({
|
|
229
235
|
isDisabled,
|
|
230
236
|
onHoverStart(e) {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
237
|
+
// Hovering over an already expanded sub dialog trigger should keep focus in the dialog.
|
|
238
|
+
if (!isFocusVisible() && !(isTriggerExpanded && hasPopup === 'dialog')) {
|
|
239
|
+
selectionManager.setFocused(true);
|
|
240
|
+
selectionManager.setFocusedKey(key);
|
|
234
241
|
}
|
|
235
242
|
hoverStartProp?.(e);
|
|
236
243
|
},
|
|
@@ -249,7 +256,7 @@ export function useMenuItem<T>(props: AriaMenuItemProps, state: TreeState<T>, re
|
|
|
249
256
|
|
|
250
257
|
switch (e.key) {
|
|
251
258
|
case ' ':
|
|
252
|
-
if (!isDisabled &&
|
|
259
|
+
if (!isDisabled && selectionManager.selectionMode === 'none' && !isTrigger && closeOnSelect !== false && onClose) {
|
|
253
260
|
onClose();
|
|
254
261
|
}
|
|
255
262
|
break;
|
|
@@ -272,15 +279,16 @@ export function useMenuItem<T>(props: AriaMenuItemProps, state: TreeState<T>, re
|
|
|
272
279
|
});
|
|
273
280
|
|
|
274
281
|
let {focusProps} = useFocus({onBlur, onFocus, onFocusChange});
|
|
275
|
-
let domProps = filterDOMProps(item
|
|
282
|
+
let domProps = filterDOMProps(item?.props);
|
|
276
283
|
delete domProps.id;
|
|
277
|
-
let linkProps = useLinkProps(item
|
|
284
|
+
let linkProps = useLinkProps(item?.props);
|
|
278
285
|
|
|
279
286
|
return {
|
|
280
287
|
menuItemProps: {
|
|
281
288
|
...ariaProps,
|
|
282
289
|
...mergeProps(domProps, linkProps, isTrigger ? {onFocus: itemProps.onFocus, 'data-key': itemProps['data-key']} : itemProps, pressProps, hoverProps, keyboardProps, focusProps),
|
|
283
|
-
|
|
290
|
+
// If a submenu is expanded, set the tabIndex to -1 so that shift tabbing goes out of the menu instead of the parent menu item.
|
|
291
|
+
tabIndex: itemProps.tabIndex != null && isTriggerExpanded ? -1 : itemProps.tabIndex
|
|
284
292
|
},
|
|
285
293
|
labelProps: {
|
|
286
294
|
id: labelId
|
package/src/useMenuTrigger.ts
CHANGED
|
@@ -47,7 +47,7 @@ export interface MenuTriggerAria<T> {
|
|
|
47
47
|
*/
|
|
48
48
|
export function useMenuTrigger<T>(props: AriaMenuTriggerProps, state: MenuTriggerState, ref: RefObject<Element | null>): MenuTriggerAria<T> {
|
|
49
49
|
let {
|
|
50
|
-
type = 'menu'
|
|
50
|
+
type = 'menu',
|
|
51
51
|
isDisabled,
|
|
52
52
|
trigger = 'press'
|
|
53
53
|
} = props;
|
|
@@ -128,6 +128,7 @@ export function useMenuTrigger<T>(props: AriaMenuTriggerProps, state: MenuTrigge
|
|
|
128
128
|
delete triggerProps.onPress;
|
|
129
129
|
|
|
130
130
|
return {
|
|
131
|
+
// @ts-ignore - TODO we pass out both DOMAttributes AND AriaButtonProps, but useButton will discard the longPress event handlers, it's only through PressResponder magic that this works for RSP and RAC. it does not work in aria examples
|
|
131
132
|
menuTriggerProps: {
|
|
132
133
|
...triggerProps,
|
|
133
134
|
...(trigger === 'press' ? pressProps : longPressProps),
|
|
@@ -63,7 +63,7 @@ export function useSafelyMouseToSubmenu(options: SafelyMouseToSubmenuOptions) {
|
|
|
63
63
|
let submenu = submenuRef.current;
|
|
64
64
|
let menu = menuRef.current;
|
|
65
65
|
|
|
66
|
-
if (isDisabled || !submenu || !isOpen || modality !== 'pointer') {
|
|
66
|
+
if (isDisabled || !submenu || !isOpen || modality !== 'pointer' || !menu) {
|
|
67
67
|
reset();
|
|
68
68
|
return;
|
|
69
69
|
}
|
package/src/useSubmenuTrigger.ts
CHANGED
|
@@ -41,7 +41,7 @@ export interface AriaSubmenuTriggerProps {
|
|
|
41
41
|
delay?: number
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
interface SubmenuTriggerProps extends AriaMenuItemProps {
|
|
44
|
+
interface SubmenuTriggerProps extends Omit<AriaMenuItemProps, 'key'> {
|
|
45
45
|
/** Whether the submenu trigger is in an expanded state. */
|
|
46
46
|
isOpen: boolean
|
|
47
47
|
}
|
|
@@ -101,14 +101,14 @@ export function useSubmenuTrigger<T>(props: AriaSubmenuTriggerProps, state: Subm
|
|
|
101
101
|
if (direction === 'ltr' && e.currentTarget.contains(e.target as Element)) {
|
|
102
102
|
e.stopPropagation();
|
|
103
103
|
onSubmenuClose();
|
|
104
|
-
ref.current
|
|
104
|
+
ref.current?.focus();
|
|
105
105
|
}
|
|
106
106
|
break;
|
|
107
107
|
case 'ArrowRight':
|
|
108
108
|
if (direction === 'rtl' && e.currentTarget.contains(e.target as Element)) {
|
|
109
109
|
e.stopPropagation();
|
|
110
110
|
onSubmenuClose();
|
|
111
|
-
ref.current
|
|
111
|
+
ref.current?.focus();
|
|
112
112
|
}
|
|
113
113
|
break;
|
|
114
114
|
case 'Escape':
|
|
@@ -124,7 +124,7 @@ export function useSubmenuTrigger<T>(props: AriaSubmenuTriggerProps, state: Subm
|
|
|
124
124
|
submenuLevel: state.submenuLevel,
|
|
125
125
|
...(type === 'menu' && {
|
|
126
126
|
onClose: state.closeAll,
|
|
127
|
-
autoFocus: state.focusStrategy,
|
|
127
|
+
autoFocus: state.focusStrategy ?? undefined,
|
|
128
128
|
onKeyDown: submenuKeyDown
|
|
129
129
|
})
|
|
130
130
|
};
|
|
@@ -205,7 +205,7 @@ export function useSubmenuTrigger<T>(props: AriaSubmenuTriggerProps, state: Subm
|
|
|
205
205
|
};
|
|
206
206
|
|
|
207
207
|
let onBlur = (e) => {
|
|
208
|
-
if (state.isOpen && parentMenuRef.current
|
|
208
|
+
if (state.isOpen && parentMenuRef.current?.contains(e.relatedTarget)) {
|
|
209
209
|
onSubmenuClose();
|
|
210
210
|
}
|
|
211
211
|
};
|