diff --git a/packages/@headlessui-react/CHANGELOG.md b/packages/@headlessui-react/CHANGELOG.md index 0baafb9351..bbfb72032f 100644 --- a/packages/@headlessui-react/CHANGELOG.md +++ b/packages/@headlessui-react/CHANGELOG.md @@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Ensure `Disclosure.Panel` is properly linked ([#1747](https://github.com/tailwindlabs/headlessui/pull/1747)) - Only select the active option when using "singular" mode when pressing `` in the `Combobox` component ([#1750](https://github.com/tailwindlabs/headlessui/pull/1750)) - Improve the types of the `Combobox` component ([#1761](https://github.com/tailwindlabs/headlessui/pull/1761)) +- Only restore focus to the `Menu.Button` if necessary when activating a `Menu.Option` ([#1782](https://github.com/tailwindlabs/headlessui/pull/1782)) ## Changed diff --git a/packages/@headlessui-react/src/components/menu/menu.tsx b/packages/@headlessui-react/src/components/menu/menu.tsx index d8e392e637..ad6474f185 100644 --- a/packages/@headlessui-react/src/components/menu/menu.tsx +++ b/packages/@headlessui-react/src/components/menu/menu.tsx @@ -35,6 +35,7 @@ import { sortByDomNode, Focus as FocusManagementFocus, focusFrom, + restoreFocusIfNecessary, } from '../../utils/focus-management' import { useOutsideClick } from '../../hooks/use-outside-click' import { useTreeWalker } from '../../hooks/use-tree-walker' @@ -463,7 +464,7 @@ let Items = forwardRefWithAs(function Items state.buttonRef.current?.focus({ preventScroll: true })) + restoreFocusIfNecessary(state.buttonRef.current) break case Keys.ArrowDown: @@ -615,7 +616,7 @@ let Item = forwardRefWithAs(function Item { if (disabled) return event.preventDefault() dispatch({ type: ActionTypes.CloseMenu }) - disposables().nextFrame(() => state.buttonRef.current?.focus({ preventScroll: true })) + restoreFocusIfNecessary(state.buttonRef.current) }) let handleFocus = useEvent(() => { diff --git a/packages/@headlessui-react/src/utils/focus-management.ts b/packages/@headlessui-react/src/utils/focus-management.ts index 68f18ab756..0800fbde3e 100644 --- a/packages/@headlessui-react/src/utils/focus-management.ts +++ b/packages/@headlessui-react/src/utils/focus-management.ts @@ -1,3 +1,4 @@ +import { disposables } from './disposables' import { match } from './match' import { getOwnerDocument } from './owner' @@ -99,6 +100,18 @@ export function isFocusableElement( }) } +export function restoreFocusIfNecessary(element: HTMLElement | null) { + let ownerDocument = getOwnerDocument(element) + disposables().nextFrame(() => { + if ( + ownerDocument && + !isFocusableElement(ownerDocument.activeElement as HTMLElement, FocusableMode.Strict) + ) { + focusElement(element) + } + }) +} + export function focusElement(element: HTMLElement | null) { element?.focus({ preventScroll: true }) } diff --git a/packages/@headlessui-vue/CHANGELOG.md b/packages/@headlessui-vue/CHANGELOG.md index 7702b1549e..4aec5c1605 100644 --- a/packages/@headlessui-vue/CHANGELOG.md +++ b/packages/@headlessui-vue/CHANGELOG.md @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Make form components uncontrollable ([#1683](https://github.com/tailwindlabs/headlessui/pull/1683)) - Improve `Combobox` re-opening keyboard issue on mobile ([#1732](https://github.com/tailwindlabs/headlessui/pull/1732)) - Only select the active option when using "singular" mode when pressing `` in the `Combobox` component ([#1750](https://github.com/tailwindlabs/headlessui/pull/1750)) +- Only restore focus to the `MenuButton` if necessary when activating a `MenuOption` ([#1782](https://github.com/tailwindlabs/headlessui/pull/1782)) ## [1.6.7] - 2022-07-12 diff --git a/packages/@headlessui-vue/src/components/menu/menu.ts b/packages/@headlessui-vue/src/components/menu/menu.ts index 35ffa90e43..4b9e8d18a8 100644 --- a/packages/@headlessui-vue/src/components/menu/menu.ts +++ b/packages/@headlessui-vue/src/components/menu/menu.ts @@ -28,6 +28,7 @@ import { sortByDomNode, Focus as FocusManagementFocus, focusFrom, + restoreFocusIfNecessary, } from '../../utils/focus-management' import { useOutsideClick } from '../../hooks/use-outside-click' @@ -384,7 +385,7 @@ export let MenuItems = defineComponent({ dom(_activeItem.dataRef.domRef)?.click() } api.closeMenu() - nextTick(() => dom(api.buttonRef)?.focus({ preventScroll: true })) + restoreFocusIfNecessary(dom(api.buttonRef)) break case Keys.ArrowDown: @@ -531,7 +532,7 @@ export let MenuItem = defineComponent({ function handleClick(event: MouseEvent) { if (props.disabled) return event.preventDefault() api.closeMenu() - nextTick(() => dom(api.buttonRef)?.focus({ preventScroll: true })) + restoreFocusIfNecessary(dom(api.buttonRef)) } function handleFocus() { diff --git a/packages/@headlessui-vue/src/utils/focus-management.ts b/packages/@headlessui-vue/src/utils/focus-management.ts index 392ef5e634..c8abd5836d 100644 --- a/packages/@headlessui-vue/src/utils/focus-management.ts +++ b/packages/@headlessui-vue/src/utils/focus-management.ts @@ -1,3 +1,4 @@ +import { nextTick } from 'vue' import { match } from './match' import { getOwnerDocument } from './owner' @@ -92,6 +93,18 @@ export function isFocusableElement( }) } +export function restoreFocusIfNecessary(element: HTMLElement | null) { + let ownerDocument = getOwnerDocument(element) + nextTick(() => { + if ( + ownerDocument && + !isFocusableElement(ownerDocument.activeElement as HTMLElement, FocusableMode.Strict) + ) { + focusElement(element) + } + }) +} + export function focusElement(element: HTMLElement | null) { element?.focus({ preventScroll: true }) }