fix(front): autofocus follows the stacking order of elements

This commit is contained in:
upsiflu 2025-03-22 17:18:03 +01:00
parent 3044a88dbc
commit ef67b38018
4 changed files with 39 additions and 11 deletions

View File

@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, computed, useSlots, onMounted } from 'vue'
import { ref, computed, useSlots, onMounted, watch, onUnmounted, nextTick } from 'vue'
import { type ColorProps, type VariantProps, type DefaultProps, type RaisedProps, type PastelProps, color } from '~/composables/color'
import { type WidthProps, width } from '~/composables/width'
@ -65,9 +65,18 @@ const click = async (...args: any[]) => {
internalLoader.value = false
}
}
onMounted(() => {
if (props.autofocus) button.value.focus()
})
const previouslyFocusedElement = ref()
onMounted(() => props.autofocus && nextTick(() => {
previouslyFocusedElement.value = document.activeElement
previouslyFocusedElement.value?.blur()
button.value.focus()
}))
onUnmounted(() =>
previouslyFocusedElement.value?.focus()
)
</script>
<template>

View File

@ -1,5 +1,5 @@
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import { computed, nextTick, onMounted, onUnmounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import onKeyboardShortcut from '~/composables/onKeyboardShortcut'
import { type ColorProps, type VariantProps, type DefaultProps, type RaisedProps, type PastelProps, color } from '~/composables/color.ts'
@ -41,9 +41,17 @@ const { t } = useI18n()
const input = ref()
onMounted(() => {
if (props.autofocus) input.value.focus()
})
const previouslyFocusedElement = ref()
onMounted(() => props.autofocus && nextTick(() => {
previouslyFocusedElement.value = document.activeElement
previouslyFocusedElement.value?.blur()
input.value.focus()
}))
onUnmounted(() =>
previouslyFocusedElement.value?.focus()
)
const model = defineModel<string|number>({ required: true })
</script>

View File

@ -13,7 +13,8 @@ const props = defineProps<{
overPopover?: true,
destructive?: true,
cancel?: string,
icon? : string
icon?: string,
autofocus?: true | 'off'
} &(ColorProps | DefaultProps)>()
const isOpen = defineModel<boolean>({ default: false })
@ -23,8 +24,11 @@ const previouslyFocusedElement = ref()
// Handle focus and inertness of the elements behind the modal
watchEffect(() => {
if (isOpen.value) {
previouslyFocusedElement.value = document.activeElement
document.querySelector('#app')?.setAttribute('inert', 'true')
nextTick(()=>{
previouslyFocusedElement.value = document.activeElement
previouslyFocusedElement.value?.blur()
document.querySelector('#app')?.setAttribute('inert', 'true')
})
} else {
nextTick(() => previouslyFocusedElement.value?.focus())
document.querySelector('#app')?.removeAttribute('inert')
@ -93,6 +97,7 @@ onKeyboardShortcut('escape', () => { isOpen.value = false })
icon="bi-x-lg"
ghost
align-self="baseline"
:autofocus="props.autofocus === undefined ? ($slots.actions || cancel ? undefined : true) : props.autofocus !== 'off'"
@click="isOpen = false"
/>
</Layout>

View File

@ -335,6 +335,12 @@ primary
</Layout>
## Auto-focus the close button
If there are no action slots and no cancel button, the close button is auto-focused.
The `autofocus` prop, when set to `off`, overrides this behavior.
## Responsivity
### Designing for small screens