Skip to content

Commit 27dece1

Browse files
authored
Fix re-focusing element after close (#1186)
* fix restoreElement logic The code for React already worked, let's update the Vue code to make it similar which properly restores focus. * update changelog
1 parent 8208c07 commit 27dece1

File tree

4 files changed

+22
-16
lines changed

4 files changed

+22
-16
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3434
- Improve outside click support ([#1175](https://github.com/tailwindlabs/headlessui/pull/1175))
3535
- Reset Combobox Input when the value gets reset ([#1181](https://github.com/tailwindlabs/headlessui/pull/1181))
3636
- Adjust active {item,option} index ([#1184](https://github.com/tailwindlabs/headlessui/pull/1184))
37+
- Fix re-focusing element after close ([#1186](https://github.com/tailwindlabs/headlessui/pull/1186))
3738

3839
## [@headlessui/react@v1.5.0] - 2022-02-17
3940

packages/@headlessui-vue/src/components/listbox/listbox.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { dom } from '../../utils/dom'
2424
import { useOpenClosed, State, useOpenClosedProvider } from '../../internal/open-closed'
2525
import { match } from '../../utils/match'
2626
import { useResolveButtonType } from '../../hooks/use-resolve-button-type'
27-
import { sortByDomNode } from '../../utils/focus-management'
27+
import { FocusableMode, isFocusableElement, sortByDomNode } from '../../utils/focus-management'
2828
import { useOutsideClick } from '../../hooks/use-outside-click'
2929

3030
enum ListboxStates {
@@ -247,14 +247,15 @@ export let Listbox = defineComponent({
247247
}
248248

249249
// Handle outside click
250-
useOutsideClick(buttonRef, (event, target) => {
251-
let active = document.activeElement
252-
250+
useOutsideClick([buttonRef, optionsRef], (event, target) => {
253251
if (listboxState.value !== ListboxStates.Open) return
254252

255-
if (!dom(optionsRef)?.contains(target)) api.closeListbox()
256-
if (active !== document.body && active?.contains(target)) return // Keep focus on newly clicked/focused element
257-
if (!event.defaultPrevented) dom(buttonRef)?.focus({ preventScroll: true })
253+
api.closeListbox()
254+
255+
if (!isFocusableElement(target, FocusableMode.Loose)) {
256+
event.preventDefault()
257+
dom(buttonRef)?.focus()
258+
}
258259
})
259260

260261
// @ts-expect-error Types of property 'dataRef' are incompatible.

packages/@headlessui-vue/src/components/menu/menu.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { useTreeWalker } from '../../hooks/use-tree-walker'
2222
import { useOpenClosedProvider, State, useOpenClosed } from '../../internal/open-closed'
2323
import { match } from '../../utils/match'
2424
import { useResolveButtonType } from '../../hooks/use-resolve-button-type'
25-
import { sortByDomNode } from '../../utils/focus-management'
25+
import { FocusableMode, isFocusableElement, sortByDomNode } from '../../utils/focus-management'
2626
import { useOutsideClick } from '../../hooks/use-outside-click'
2727

2828
enum MenuStates {
@@ -201,14 +201,15 @@ export let Menu = defineComponent({
201201
}
202202

203203
// Handle outside click
204-
useOutsideClick(buttonRef, (event, target) => {
205-
let active = document.activeElement
206-
204+
useOutsideClick([buttonRef, itemsRef], (event, target) => {
207205
if (menuState.value !== MenuStates.Open) return
208206

209-
if (!dom(itemsRef)?.contains(target)) api.closeMenu()
210-
if (active !== document.body && active?.contains(target)) return // Keep focus on newly clicked/focused element
211-
if (!event.defaultPrevented) dom(buttonRef)?.focus({ preventScroll: true })
207+
api.closeMenu()
208+
209+
if (!isFocusableElement(target, FocusableMode.Loose)) {
210+
event.preventDefault()
211+
dom(buttonRef)?.focus()
212+
}
212213
})
213214

214215
// @ts-expect-error Types of property 'dataRef' are incompatible.

packages/@headlessui-vue/src/hooks/use-focus-trap.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ export function useFocusTrap(
2424
function handleFocus() {
2525
if (!enabled.value) return
2626
if (containers.value.size !== 1) return
27-
let { initialFocus } = options.value
2827

28+
let { initialFocus } = options.value
2929
let activeElement = document.activeElement as HTMLElement
3030

3131
if (initialFocus) {
@@ -36,7 +36,10 @@ export function useFocusTrap(
3636
return // Already focused within Dialog
3737
}
3838

39-
restoreElement.value = activeElement
39+
if (!restoreElement.value) {
40+
// We already have a restore element
41+
restoreElement.value = activeElement
42+
}
4043

4144
// Try to focus the initialFocus ref
4245
if (initialFocus) {

0 commit comments

Comments
 (0)