feat : scroll-lock, focus-trap et restitution du focus du MalioDrawer

This commit is contained in:
2026-05-21 16:37:19 +02:00
parent f443803327
commit e7af92808f
2 changed files with 127 additions and 2 deletions

View File

@@ -79,7 +79,17 @@
</template>
<script setup lang="ts">
import { computed, ref, useAttrs, useId, useSlots } from 'vue'
import {
computed,
nextTick,
onBeforeUnmount,
onMounted,
ref,
useAttrs,
useId,
useSlots,
watch,
} from 'vue'
import { Icon as IconifyIcon } from '@iconify/vue'
import { twMerge } from 'tailwind-merge'
@@ -139,6 +149,51 @@ const isRendered = ref(isOpen.value)
const panelRef = ref<HTMLElement | null>(null)
let previouslyFocused: HTMLElement | null = null
function getFocusable(container: HTMLElement): HTMLElement[] {
return Array.from(
container.querySelectorAll<HTMLElement>(
'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])',
),
).filter((el) => el.tabIndex !== -1)
}
function onOpen() {
previouslyFocused = (document.activeElement as HTMLElement | null) ?? null
document.body.style.overflow = 'hidden'
nextTick(() => {
const panel = panelRef.value
if (!panel) return
const focusable = getFocusable(panel)
;(focusable[0] ?? panel).focus()
})
}
function onClose() {
document.body.style.overflow = ''
previouslyFocused?.focus?.()
previouslyFocused = null
}
watch(isOpen, (val) => {
if (val) {
isRendered.value = true
onOpen()
}
else {
onClose()
}
})
onMounted(() => {
if (isOpen.value) onOpen()
})
onBeforeUnmount(() => {
document.body.style.overflow = ''
})
function onBackdropClick() {
if (props.dismissable) close()
}
@@ -147,6 +202,27 @@ function onKeydown(e: KeyboardEvent) {
if (e.key === 'Escape' && props.closeOnEscape) {
e.stopPropagation()
close()
return
}
if (e.key !== 'Tab') return
const panel = panelRef.value
if (!panel) return
const focusable = getFocusable(panel)
if (focusable.length === 0) {
e.preventDefault()
panel.focus()
return
}
const first = focusable[0]!
const last = focusable[focusable.length - 1]!
if (e.shiftKey && document.activeElement === first) {
e.preventDefault()
last.focus()
}
else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault()
first.focus()
}
}