11<script setup lang="ts">
2- import { ref , onMounted , onBeforeUnmount } from ' vue'
2+ import { ref , watch , nextTick } from ' vue'
3+ import { onClickOutside , onKeyDown } from ' @vueuse/core'
4+ import { useFocusTrap } from ' @vueuse/integrations/useFocusTrap'
35
46const route = useRoute ()
57const isHome = computed (() => route .name === ' index' )
@@ -13,25 +15,32 @@ const togglePopover = (e?: Event) => {
1315 showPopover .value = ! showPopover .value
1416}
1517
16- const onDocClick = (e : Event ) => {
17- const t = e .target as Node
18- if (! popoverRef .value || ! triggerRef .value ) return
19- if (! popoverRef .value .contains (t ) && ! triggerRef .value .contains (t )) {
18+ onClickOutside (
19+ popoverRef ,
20+ () => {
2021 showPopover .value = false
21- }
22- }
22+ },
23+ { ignore: [triggerRef ] },
24+ )
2325
24- const onKeydown = (e : KeyboardEvent ) => {
25- if (e .key === ' Escape' ) showPopover .value = false
26- }
26+ onKeyDown (
27+ ' Escape' ,
28+ () => {
29+ if (showPopover .value ) showPopover .value = false
30+ },
31+ { dedupe: true },
32+ )
2733
28- onMounted (() => {
29- document .addEventListener (' click' , onDocClick )
30- document .addEventListener (' keydown' , onKeydown )
31- })
32- onBeforeUnmount (() => {
33- document .removeEventListener (' click' , onDocClick )
34- document .removeEventListener (' keydown' , onKeydown )
34+ const { activate, deactivate } = useFocusTrap (popoverRef , { allowOutsideClick: true })
35+ watch (showPopover , async open => {
36+ if (open ) {
37+ await nextTick ()
38+ activate ()
39+ popoverRef .value ?.focus ?.()
40+ } else {
41+ deactivate ()
42+ triggerRef .value ?.focus ?.()
43+ }
3544})
3645 </script >
3746
@@ -79,21 +88,22 @@ onBeforeUnmount(() => {
7988
8089 <Teleport to =" body" >
8190 <Transition
82- enter-active-class =" transition-opacity duration-150 motion-reduce:transition-none"
83- leave-active-class =" transition-opacity duration-100 motion-reduce:transition-none"
91+ enter-active-class =" transition-opacity duration-0 motion-reduce:transition-none"
92+ leave-active-class =" transition-opacity duration-0 motion-reduce:transition-none"
8493 enter-from-class =" opacity-0"
8594 leave-to-class =" opacity-0"
8695 >
87- <div v-if =" showPopover" >
96+ <div v-show =" showPopover" >
8897 <div
89- class =" fixed inset-0 bg-bg-elevated/70 dark:bg-bg-elevated/90 z-40 "
98+ class =" fixed inset-0 bg-bg-elevated/70 dark:bg-bg-elevated/90 z-[9998] "
9099 @click =" showPopover = false"
91100 aria-hidden =" true"
92101 ></div >
93102
94- <div class =" fixed inset-0 z-50 flex items-center justify-center" >
103+ <div class =" fixed inset-0 z-[9999] flex items-center justify-center" >
95104 <div
96105 ref =" popoverRef"
106+ tabindex =" -1"
97107 class =" mx-4 max-w-lg w-full p-6 bg-bg border border-border rounded-lg shadow-xl text-sm text-fg"
98108 role =" dialog"
99109 :aria-label =" $t('footer.keyboard_shortcuts')"
@@ -107,7 +117,7 @@ onBeforeUnmount(() => {
107117 @click =" showPopover = false"
108118 >
109119 <span aria-hidden =" true" class =" size-5 i-lucide-x" />
110- <span class =" sr-only" >{{ $t('close') }}</span >
120+ <span class =" sr-only" >{{ $t('common. close') }}</span >
111121 </button >
112122 </div >
113123 <p class =" mb-2 font-mono text-fg-subtle" >
0 commit comments