add and fix keyboard nav
This commit is contained in:
parent
33bf025fdb
commit
ca031126e1
@ -143,7 +143,12 @@ body
|
||||
opacity: 0
|
||||
|
||||
*:focus
|
||||
outline: 1px solid transparent
|
||||
outline: none
|
||||
|
||||
body.user-is-tabbing *:focus
|
||||
outline: none
|
||||
box-shadow: 0 0 0 4px $pink !important
|
||||
border-radius: 5px
|
||||
|
||||
/*.router-link-active
|
||||
position: relative
|
||||
|
||||
@ -6,7 +6,17 @@
|
||||
aria-label="Breadcrumb"
|
||||
>
|
||||
<div class="breadcrumb-container">
|
||||
<div class="breadcrumb-trigger" @click="toggleOpen" aria-label="Breadcrumb öffnen">
|
||||
<!-- Trigger -->
|
||||
<div
|
||||
class="breadcrumb-trigger"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@click="toggleOpen"
|
||||
@keydown.space.prevent="toggleOpen"
|
||||
@keydown.enter.prevent="toggleOpen"
|
||||
:aria-expanded="isOpen.toString()"
|
||||
aria-label="Breadcrumb öffnen"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 576 512"
|
||||
@ -20,7 +30,7 @@
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<!-- Breadcrumbs selbst (ein- und ausklappbar) -->
|
||||
<!-- Breadcrumbs -->
|
||||
<ul :class="{ open: isOpen, closed: !isOpen }">
|
||||
<li
|
||||
v-for="(crumb, index) in breadcrumbs"
|
||||
@ -30,19 +40,33 @@
|
||||
v-if="index < breadcrumbs.length - 1"
|
||||
:to="crumb.to"
|
||||
:title="crumb.labelFull"
|
||||
>{{ crumb.label }}
|
||||
>
|
||||
{{ crumb.label }}
|
||||
</router-link>
|
||||
<span v-else :title="crumb.labelFull"><span v-if="breadcrumbs.length > 1" class="between">›</span>{{ crumb.label }}</span>
|
||||
<span v-else :title="crumb.labelFull">
|
||||
<span v-if="breadcrumbs.length > 1" class="between">›</span>
|
||||
{{ crumb.label }}
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<div
|
||||
class="close-btn"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@click="isOpen = false"
|
||||
@keydown.space.prevent="isOpen = false"
|
||||
@keydown.enter.prevent="isOpen = false"
|
||||
aria-label="Breadcrumb schließen"
|
||||
title="Schließen"
|
||||
></div>
|
||||
</li>
|
||||
<li class="close-btn" @click="isOpen = false" title="Schließen"></li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { ref, computed, onMounted, onBeforeUnmount, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { i18nPages } from '~/i18n/i18n-pages'
|
||||
@ -54,11 +78,30 @@ interface Breadcrumb {
|
||||
}
|
||||
|
||||
const isOpen = ref(false)
|
||||
const route = useRoute()
|
||||
const { locale, t } = useI18n()
|
||||
|
||||
function toggleOpen() {
|
||||
isOpen.value = !isOpen.value
|
||||
}
|
||||
|
||||
function onKeydown(e: KeyboardEvent) {
|
||||
if (e.key === 'Escape') {
|
||||
isOpen.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('keydown', onKeydown)
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('keydown', onKeydown)
|
||||
})
|
||||
|
||||
watch(() => route.fullPath, () => {
|
||||
isOpen.value = false
|
||||
})
|
||||
|
||||
function formatLabel(segment: string): { label: string; labelFull: string } {
|
||||
const labelFull = segment.charAt(0).toUpperCase() + segment.slice(1).replace(/-/g, ' ')
|
||||
let label = labelFull
|
||||
@ -69,15 +112,9 @@ function formatLabel(segment: string): { label: string; labelFull: string } {
|
||||
}
|
||||
|
||||
function buildUrl(loc: string, path: string): string {
|
||||
if (loc === 'de') {
|
||||
return path.startsWith('/') ? path : '/' + path
|
||||
}
|
||||
return `/${loc}${path.startsWith('/') ? path : '/' + path}`
|
||||
return loc === 'de' ? '/' + path.replace(/^\//, '') : `/${loc}/${path.replace(/^\//, '')}`
|
||||
}
|
||||
|
||||
const route = useRoute()
|
||||
const { locale, t } = useI18n()
|
||||
|
||||
const breadcrumbs = computed<Breadcrumb[]>(() => {
|
||||
const loc = locale.value
|
||||
const pathWithoutLang = route.path.replace(`/${loc}`, '')
|
||||
@ -86,61 +123,32 @@ const breadcrumbs = computed<Breadcrumb[]>(() => {
|
||||
if (segments.length === 2 && segments[0] === 'projekt') {
|
||||
const referencesPath = i18nPages.references?.[loc] || '/references'
|
||||
const referencesLabel = t('references') || 'References'
|
||||
|
||||
const first = {
|
||||
label: referencesLabel,
|
||||
labelFull: referencesLabel,
|
||||
to: buildUrl(loc, referencesPath)
|
||||
}
|
||||
|
||||
const { label, labelFull } = formatLabel(segments[1])
|
||||
const second = {
|
||||
label,
|
||||
labelFull,
|
||||
to: route.path
|
||||
}
|
||||
|
||||
return [first, second]
|
||||
return [
|
||||
{ label: referencesLabel, labelFull: referencesLabel, to: buildUrl(loc, referencesPath) },
|
||||
{ ...formatLabel(segments[1]), to: route.path }
|
||||
]
|
||||
}
|
||||
|
||||
if (segments.length === 2 && segments[0] === 'artikel') {
|
||||
const magazinePath = i18nPages.magazin?.[loc] || '/magazin'
|
||||
const magazineLabel = t('magazin') || 'Magazin'
|
||||
|
||||
const first = {
|
||||
label: magazineLabel,
|
||||
labelFull: magazineLabel,
|
||||
to: buildUrl(loc, magazinePath)
|
||||
}
|
||||
|
||||
const { label, labelFull } = formatLabel(segments[1])
|
||||
const second = {
|
||||
label,
|
||||
labelFull,
|
||||
to: route.path
|
||||
}
|
||||
|
||||
return [first, second]
|
||||
return [
|
||||
{ label: magazineLabel, labelFull: magazineLabel, to: buildUrl(loc, magazinePath) },
|
||||
{ ...formatLabel(segments[1]), to: route.path }
|
||||
]
|
||||
}
|
||||
|
||||
let path = ''
|
||||
return segments.map(segment => {
|
||||
path += '/' + segment
|
||||
const { label, labelFull } = formatLabel(segment)
|
||||
return {
|
||||
label,
|
||||
labelFull,
|
||||
to: buildUrl(loc, path)
|
||||
}
|
||||
return { label, labelFull, to: buildUrl(loc, path) }
|
||||
})
|
||||
})
|
||||
|
||||
watch(() => route.fullPath, () => {
|
||||
isOpen.value = false
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
|
||||
<style lang="sass">
|
||||
.breadcrumbs
|
||||
position: fixed
|
||||
top: 12vh
|
||||
@ -165,14 +173,17 @@ watch(() => route.fullPath, () => {
|
||||
justify-content: center
|
||||
width: 1.8rem
|
||||
height: 1.8rem
|
||||
background: none
|
||||
border: none
|
||||
margin: 0 0 0 .5rem
|
||||
|
||||
svg
|
||||
width: 1rem
|
||||
height: 1rem
|
||||
transform: translateX(10px)
|
||||
width: 1rem !important
|
||||
height: 1rem !important
|
||||
display: inline-block
|
||||
|
||||
path
|
||||
fill: darken($lightgrey, 20%)
|
||||
fill: darken($lightgrey, 30%) !important
|
||||
|
||||
&:hover path
|
||||
fill: darken($lightgrey, 30%)
|
||||
@ -224,6 +235,8 @@ watch(() => route.fullPath, () => {
|
||||
border-radius: 50%
|
||||
position: relative
|
||||
margin: .2rem .5rem 0 1rem
|
||||
border: none
|
||||
text-transform: none
|
||||
|
||||
&::before
|
||||
position: absolute
|
||||
|
||||
@ -7,13 +7,15 @@
|
||||
aria-labelledby="controlIcon"
|
||||
role="dialog"
|
||||
>
|
||||
<svg
|
||||
id="controlIcon"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
aria-label="Toggle contact form"
|
||||
@click="toggleContactBubble"
|
||||
>
|
||||
<svg
|
||||
id="controlIcon"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
aria-label="Toggle contact form"
|
||||
@click="toggleContactBubble"
|
||||
@keydown.space.prevent="toggleContactBubble"
|
||||
@keydown.enter.prevent="toggleContactBubble"
|
||||
>
|
||||
<use :xlink:href="`/assets/icons/collection.svg#${isContactBubbleOpen ? 'times' : 'talk'}`" />
|
||||
</svg>
|
||||
|
||||
@ -77,6 +79,7 @@
|
||||
class="form-control"
|
||||
type="text"
|
||||
name="name"
|
||||
ref="firstInput"
|
||||
required
|
||||
autocomplete="name"
|
||||
:aria-invalid="!!errors.name"
|
||||
@ -188,10 +191,10 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useMainStore } from '@/stores/main';
|
||||
import { ref, reactive, computed } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const firstInput = ref<HTMLInputElement | null>(null);
|
||||
|
||||
// i18n Setup
|
||||
const { t } = useI18n();
|
||||
@ -291,6 +294,30 @@ const resetForm = () => {
|
||||
errors.email = null;
|
||||
errors.phone = null;
|
||||
};
|
||||
|
||||
// ESC & Fokusmanagement
|
||||
const handleKeydown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape' && isContactBubbleOpen.value) {
|
||||
toggleContactBubble();
|
||||
}
|
||||
};
|
||||
|
||||
// Füge den EventListener beim Mount hinzu
|
||||
onMounted(() => {
|
||||
window.addEventListener('keydown', handleKeydown);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('keydown', handleKeydown);
|
||||
});
|
||||
|
||||
// Wenn die Bubble geöffnet wird: Fokus setzen
|
||||
watch(isContactBubbleOpen, async (newVal) => {
|
||||
if (newVal) {
|
||||
await nextTick()
|
||||
firstInput.value?.focus()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
@ -46,6 +46,34 @@ const accordionItems = computed(() =>
|
||||
)
|
||||
|
||||
const toggleContactBubble = () => mainStore.toggleContactBubble()
|
||||
|
||||
// FAQ JSON-LD
|
||||
|
||||
useHead(() => {
|
||||
if (!accordionItems.value.length) return {}
|
||||
|
||||
const faqJsonLd = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "FAQPage",
|
||||
"mainEntity": accordionItems.value.map(item => ({
|
||||
"@type": "Question",
|
||||
"name": item.title,
|
||||
"acceptedAnswer": {
|
||||
"@type": "Answer",
|
||||
"text": item.html.replace(/<[^>]*>?/gm, '') // HTML-Tags entfernen
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
return {
|
||||
script: [
|
||||
{
|
||||
type: 'application/ld+json',
|
||||
children: JSON.stringify(faqJsonLd)
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
@ -42,27 +42,27 @@ const props = defineProps({
|
||||
|
||||
<style lang="sass">
|
||||
.sideBarNaviSlider
|
||||
position: fixed
|
||||
background-color: rgba($yellow, .8)
|
||||
color: white
|
||||
text-transform: uppercase
|
||||
font-size: 1rem !important
|
||||
letter-spacing: .05rem
|
||||
top: 40vh
|
||||
right: -80px // Startposition außerhalb des Bildschirms
|
||||
padding: 1.2rem .4rem 1.6rem .3rem
|
||||
writing-mode: vertical-lr
|
||||
transform: rotate(180deg)
|
||||
border-bottom-right-radius: 1rem
|
||||
border-top-right-radius: 1rem
|
||||
cursor: pointer
|
||||
transition: right 0.6s ease-out, transform .8s
|
||||
z-index: 10
|
||||
position: fixed
|
||||
background-color: rgba($yellow, .8)
|
||||
color: white
|
||||
text-transform: uppercase
|
||||
font-size: 1rem !important
|
||||
letter-spacing: .05rem
|
||||
top: 40vh
|
||||
right: -80px // Startposition außerhalb des Bildschirms
|
||||
padding: 1.2rem .4rem 1.6rem .3rem
|
||||
writing-mode: vertical-lr
|
||||
transform: rotate(180deg)
|
||||
border-bottom-right-radius: 1rem
|
||||
border-top-right-radius: 1rem
|
||||
cursor: pointer
|
||||
transition: right 0.6s ease-out, transform .8s
|
||||
z-index: 10
|
||||
|
||||
&.slide-in
|
||||
right: 0 // Fährt an die finale Position
|
||||
&.slide-in
|
||||
right: 0 // Fährt an die finale Position
|
||||
|
||||
&:hover
|
||||
transform: rotate(180deg) scale(1.1)
|
||||
&:hover
|
||||
transform: rotate(180deg) scale(1.1)
|
||||
</style>
|
||||
|
||||
@ -1,59 +1,95 @@
|
||||
<template>
|
||||
<nav class="mainNav" :class="[
|
||||
isMenuOpen ? 'active' : '',
|
||||
screenWidth < 1350 ? 'mobile-nav' : 'desk-nav',
|
||||
scrollPosition > 50 ? 'scrolled' : ''
|
||||
]">
|
||||
<!-- Burger Icon nur bei Mobile sichtbar -->
|
||||
<div class="burger" @click="toggleMenu" v-if="screenWidth < 1350" :class="{ open: isMenuOpen }">
|
||||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
<nav
|
||||
class="mainNav"
|
||||
:class="[
|
||||
isMenuOpen ? 'active' : '',
|
||||
screenWidth < 1350 ? 'mobile-nav' : 'desk-nav',
|
||||
scrollPosition > 50 ? 'scrolled' : ''
|
||||
]"
|
||||
aria-label="Hauptnavigation"
|
||||
>
|
||||
<!-- Burger Icon als Button -->
|
||||
<button
|
||||
class="burger"
|
||||
@click="toggleMenu"
|
||||
v-if="screenWidth < 1350"
|
||||
:aria-expanded="isMenuOpen"
|
||||
aria-controls="main-navigation"
|
||||
:class="{ open: isMenuOpen }"
|
||||
type="button"
|
||||
>
|
||||
<span class="visually-hidden">Menü öffnen</span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</button>
|
||||
|
||||
<!-- Navigation -->
|
||||
<ul
|
||||
:class="[
|
||||
screenWidth < 1350 && isMenuOpen ? 'mobile-menu active' :
|
||||
screenWidth < 1350 ? 'mobile-menu' : 'desk-menu']"
|
||||
v-show="screenWidth >= 1350 || isMenuOpen"
|
||||
<!-- Navigation -->
|
||||
<ul
|
||||
:id="'main-navigation'"
|
||||
:class="[
|
||||
screenWidth < 1350 && isMenuOpen ? 'mobile-menu active' :
|
||||
screenWidth < 1350 ? 'mobile-menu' : 'desk-menu'
|
||||
]"
|
||||
v-show="screenWidth >= 1350 || isMenuOpen"
|
||||
>
|
||||
<li
|
||||
v-for="link in navigationLinks"
|
||||
:key="link.label"
|
||||
class="nav-item"
|
||||
@mouseenter="screenWidth >= 1350 && link.subNav && showSubNav(link.label)"
|
||||
@mouseleave="screenWidth >= 1350 && link.subNav && hideSubNav(link.label)"
|
||||
>
|
||||
<li
|
||||
v-for="link in navigationLinks"
|
||||
:key="link.label"
|
||||
class="nav-item"
|
||||
@mouseenter="screenWidth >= 1350 && link.subNav && showSubNav(link.label)"
|
||||
@mouseleave="screenWidth >= 1350 && link.subNav && hideSubNav(link.label)"
|
||||
<!-- Mit SubNav -->
|
||||
<template v-if="link.subNav">
|
||||
<button
|
||||
v-if="screenWidth < 1350"
|
||||
class="subnav-toggle"
|
||||
@click="toggleMobileSubNav(link.label)"
|
||||
:aria-expanded="isActiveSubNav(link.label)"
|
||||
:aria-controls="`submenu-${link.label}`"
|
||||
type="button"
|
||||
>
|
||||
{{ $t(link.label) }}
|
||||
<span class="arrow" :class="{ open: isActiveSubNav(link.label) }"></span>
|
||||
<span class="visually-hidden">
|
||||
{{ isActiveSubNav(link.label) ? 'Untermenü schließen' : 'Untermenü öffnen' }}
|
||||
</span>
|
||||
</button>
|
||||
<div v-else>
|
||||
{{ $t(link.label) }}
|
||||
<span class="arrow" :class="{ open: isSubNavOpen === link.label }"></span>
|
||||
</div>
|
||||
|
||||
>
|
||||
<!-- Mit SubNav -->
|
||||
<template v-if="link.subNav">
|
||||
<div @click="screenWidth < 1350 ? toggleMobileSubNav(link.label) : null">
|
||||
{{ $t(link.label) }}
|
||||
<span class="arrow" :class="{ open: (screenWidth < 1350 ? isMobileSubNavOpen : isSubNavOpen) === link.label }"></span>
|
||||
</div>
|
||||
<ul
|
||||
:id="`submenu-${link.label}`"
|
||||
class="submenu"
|
||||
:class="{ open: isActiveSubNav(link.label) }"
|
||||
>
|
||||
<li v-for="sublink in link.subNav" :key="sublink.label">
|
||||
<NuxtLinkLocale
|
||||
:to="sublink.routeKey"
|
||||
@click.native="handleMobileClose"
|
||||
>
|
||||
{{ $t(sublink.label) }}
|
||||
</NuxtLinkLocale>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<ul
|
||||
class="submenu"
|
||||
:class="{ open: isActiveSubNav(link.label) }"
|
||||
>
|
||||
<li v-for="sublink in link.subNav" :key="sublink.label">
|
||||
<NuxtLinkLocale :to="sublink.routeKey" @click.native="handleMobileClose">
|
||||
{{ $t(sublink.label) }}
|
||||
</NuxtLinkLocale>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
<!-- Ohne SubNav -->
|
||||
<template v-else>
|
||||
<NuxtLinkLocale
|
||||
:to="link.routeKey"
|
||||
@click.native="handleMobileClose"
|
||||
>
|
||||
{{ $t(link.label) }}
|
||||
</NuxtLinkLocale>
|
||||
</template>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<!-- Ohne SubNav -->
|
||||
<template v-else>
|
||||
<NuxtLinkLocale :to="link.routeKey" @click.native="handleMobileClose">
|
||||
{{ $t(link.label) }}
|
||||
</NuxtLinkLocale>
|
||||
</template>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup>
|
||||
@ -154,6 +190,15 @@
|
||||
right: 0
|
||||
background-color: transparent
|
||||
|
||||
.visually-hidden
|
||||
position: absolute
|
||||
width: 1px
|
||||
height: 1px
|
||||
padding: 0
|
||||
margin: -1px
|
||||
overflow: hidden
|
||||
clip: rect(0, 0, 0, 0)
|
||||
border: 0
|
||||
|
||||
.burger
|
||||
width: 4rem
|
||||
@ -184,9 +229,9 @@
|
||||
border: 1px solid transparent
|
||||
span
|
||||
height: 3px
|
||||
&:nth-child(1)
|
||||
transform: rotate(45deg) translate(2px, 5px)
|
||||
&:nth-child(2)
|
||||
transform: rotate(45deg) translate(2px, 5px)
|
||||
&:nth-child(3)
|
||||
transform: rotate(-45deg) translate(3px, -6px)
|
||||
|
||||
.nav-item
|
||||
@ -257,9 +302,9 @@
|
||||
.submenu
|
||||
position: absolute
|
||||
top: 100%
|
||||
left: -50%
|
||||
left: -30%
|
||||
width: 200%
|
||||
background-image: linear-gradient(to bottom, rgba(#fff, 0.1) 0%, rgba(#fff, 0.9) 8%, rgba(#fff, .98) 100%)
|
||||
background-image: linear-gradient(to bottom, rgba(#fff, 0.1) 0%, rgba(#fff, 0.9) 18%, rgba(#fff, .98) 100%)
|
||||
border-bottom-right-radius: 1rem
|
||||
border-bottom-left-radius: 1rem
|
||||
list-style: none
|
||||
@ -294,6 +339,13 @@
|
||||
cursor: pointer
|
||||
font-size: 1.25rem
|
||||
color: white
|
||||
.subnav-toggle
|
||||
background-color: transparent
|
||||
border: none
|
||||
color: white
|
||||
font-size: 1.25rem
|
||||
text-transform: uppercase
|
||||
margin-top: -6px
|
||||
&::before
|
||||
content: ''
|
||||
width: .7rem
|
||||
|
||||
45
composables/useDeferredTabFocus.ts
Normal file
45
composables/useDeferredTabFocus.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
||||
|
||||
export function useDeferredTabFocus() {
|
||||
const focusableElements = ref<HTMLElement[]>([])
|
||||
|
||||
function register(el: HTMLElement | null) {
|
||||
if (el) {
|
||||
el.setAttribute('tabindex', '-1')
|
||||
focusableElements.value.push(el)
|
||||
}
|
||||
}
|
||||
|
||||
function activateFocus() {
|
||||
focusableElements.value.forEach(el => el.removeAttribute('tabindex'))
|
||||
document.body.classList.add('user-is-tabbing') // <– das hier neu
|
||||
window.removeEventListener('keydown', onKeyDown)
|
||||
}
|
||||
|
||||
|
||||
function onKeyDown(e: KeyboardEvent) {
|
||||
if (e.key === 'Tab') {
|
||||
activateFocus()
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseDown() {
|
||||
document.body.classList.remove('user-is-tabbing')
|
||||
window.addEventListener('keydown', onKeyDown) // optional: erlaubt Wiederaktivierung nach Maus
|
||||
}
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('keydown', onKeyDown)
|
||||
window.addEventListener('mousedown', onMouseDown)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('keydown', onKeyDown)
|
||||
window.removeEventListener('mousedown', onMouseDown)
|
||||
})
|
||||
|
||||
return {
|
||||
register
|
||||
}
|
||||
}
|
||||
@ -17,6 +17,10 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
import { useDeferredTabFocus } from '@/composables/useDeferredTabFocus'
|
||||
useDeferredTabFocus()
|
||||
|
||||
import { useMainStore } from '@/stores/main'
|
||||
const mainStore = useMainStore()
|
||||
import { usePageMeta } from '~/composables/usePageMeta'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user