Skip to content

Commit 5b24d43

Browse files
committed
fix(VOverlay): restore focus without preventing Enter keydown
1 parent 30deb56 commit 5b24d43

File tree

3 files changed

+30
-22
lines changed

3 files changed

+30
-22
lines changed

packages/vuetify/src/components/VDialog/VDialog.tsx

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -124,13 +124,6 @@ export const VDialog = genericComponent<OverlaySlots>()({
124124
emit('afterLeave')
125125
}
126126

127-
watch(isActive, async val => {
128-
if (!val) {
129-
await nextTick()
130-
overlay.value!.activatorEl?.focus({ preventScroll: true })
131-
}
132-
})
133-
134127
useRender(() => {
135128
const overlayProps = VOverlay.filterProps(props)
136129
const activatorProps = mergeProps({

packages/vuetify/src/components/VMenu/VMenu.tsx

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ export const VMenu = genericComponent<OverlaySlots>()({
139139
) {
140140
if (focusTrapSuppressed) {
141141
if (!props.openOnHover && !overlay.value.activatorEl?.contains(after)) {
142-
isActive.value = false
142+
overlay.value.closeWithoutReturningFocus()
143143
}
144144
} else {
145145
const focusable = focusableChildren(overlay.value.contentEl)
@@ -174,26 +174,17 @@ export const VMenu = genericComponent<OverlaySlots>()({
174174
function onKeydown (e: KeyboardEvent) {
175175
if (props.disabled) return
176176

177-
if (e.key === 'Tab' || (e.key === 'Enter' && !props.closeOnContentClick)) {
178-
if (
179-
e.key === 'Enter' &&
180-
((e.target instanceof HTMLTextAreaElement) ||
181-
(e.target instanceof HTMLInputElement && !!e.target.closest('form')))
182-
) return
183-
if (e.key === 'Enter') e.preventDefault()
184-
177+
if (e.key === 'Tab') {
185178
const nextElement = getNextElement(
186179
focusableChildren(overlay.value?.contentEl as Element, false),
187180
e.shiftKey ? 'prev' : 'next',
188181
(el: HTMLElement) => el.tabIndex >= 0
189182
)
190183
if (!nextElement) {
191184
isActive.value = false
192-
overlay.value?.activatorEl?.focus()
193185
}
194186
} else if (props.submenu && e.key === (isRtl.value ? 'ArrowRight' : 'ArrowLeft')) {
195187
isActive.value = false
196-
overlay.value?.activatorEl?.focus()
197188
}
198189
}
199190

packages/vuetify/src/components/VOverlay/VOverlay.tsx

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import vClickOutside from '@/directives/click-outside'
2727
import {
2828
computed,
2929
mergeProps,
30+
nextTick,
3031
onBeforeUnmount,
3132
ref,
3233
Teleport,
@@ -170,8 +171,12 @@ export const VOverlay = genericComponent<OverlaySlots>()({
170171
const isMounted = useHydration()
171172
const { scopeId } = useScopeId()
172173

174+
let _returnFocusToActivator = true
175+
173176
watch(() => props.disabled, v => {
174-
if (v) isActive.value = false
177+
if (v) {
178+
closeWithoutReturningFocus()
179+
}
175180
})
176181

177182
const { contentStyles, updateLocation } = useLocationStrategies(props, {
@@ -192,7 +197,9 @@ export const VOverlay = genericComponent<OverlaySlots>()({
192197
function onClickOutside (e: MouseEvent) {
193198
emit('click:outside', e)
194199

195-
if (!props.persistent) isActive.value = false
200+
if (!props.persistent) {
201+
closeWithoutReturningFocus()
202+
}
196203
else animateClick()
197204
}
198205

@@ -203,6 +210,19 @@ export const VOverlay = genericComponent<OverlaySlots>()({
203210
)
204211
}
205212

213+
function closeWithoutReturningFocus () {
214+
_returnFocusToActivator = false
215+
isActive.value = false
216+
setTimeout(() => _returnFocusToActivator = true, 100)
217+
}
218+
219+
watch(isActive, async val => {
220+
if (!val && _returnFocusToActivator) {
221+
await nextTick()
222+
activatorEl.value?.focus({ preventScroll: true })
223+
}
224+
})
225+
206226
IN_BROWSER && watch(isActive, val => {
207227
if (val) {
208228
window.addEventListener('keydown', onKeydown)
@@ -241,8 +261,11 @@ export const VOverlay = genericComponent<OverlaySlots>()({
241261
useBackButton(router, next => {
242262
if (globalTop.value && isActive.value) {
243263
next(false)
244-
if (!props.persistent) isActive.value = false
245-
else animateClick()
264+
if (!props.persistent) {
265+
closeWithoutReturningFocus()
266+
} else {
267+
animateClick()
268+
}
246269
} else {
247270
next()
248271
}
@@ -370,6 +393,7 @@ export const VOverlay = genericComponent<OverlaySlots>()({
370393
globalTop,
371394
localTop,
372395
updateLocation,
396+
closeWithoutReturningFocus,
373397
}
374398
},
375399
})

0 commit comments

Comments
 (0)