mindboost-rnbo-test-project/components/SideBar.vue

289 lines
8.6 KiB
Vue

<template>
<button
class="sidebar__menu-icon text-white"
:aria-expanded="isOpen.toString()"
aria-controls="sidebar"
:aria-label="t('Toggle Sidebar')"
@click.stop="toggleSidebar"
>
<span v-if="!isOpen"><svg width="28" height="18" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M2 18c-.425 0-.781-.144-1.068-.432A1.459 1.459 0 0 1 .5 16.5c-.001-.424.143-.78.432-1.068A1.454 1.454 0 0 1 2 15h24c.425 0 .782.144 1.07.432.288.288.431.644.43 1.068-.001.424-.145.78-.432 1.07A1.435 1.435 0 0 1 26 18H2Zm0-7.5c-.425 0-.781-.144-1.068-.432A1.459 1.459 0 0 1 .5 9c-.001-.424.143-.78.432-1.068A1.454 1.454 0 0 1 2 7.5h24c.425 0 .782.144 1.07.432.288.288.431.644.43 1.068-.001.424-.145.78-.432 1.07A1.435 1.435 0 0 1 26 10.5H2ZM2 3c-.425 0-.781-.144-1.068-.432A1.459 1.459 0 0 1 .5 1.5C.499 1.076.643.72.932.432A1.454 1.454 0 0 1 2 0h24c.425 0 .782.144 1.07.432.288.288.431.644.43 1.068-.001.424-.145.78-.432 1.07A1.435 1.435 0 0 1 26 3H2Z" fill="currentColor" /></svg></span>
<span v-else><svg width="36" height="36" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m18 20.1-7.35 7.35c-.275.275-.625.413-1.05.413-.425 0-.775-.138-1.05-.413-.275-.275-.412-.625-.412-1.05 0-.425.137-.775.412-1.05L15.9 18l-7.35-7.35c-.275-.275-.412-.625-.412-1.05 0-.425.137-.775.412-1.05.275-.275.625-.413 1.05-.413.425 0 .775.138 1.05.413L18 15.9l7.35-7.35c.275-.275.625-.413 1.05-.413.425 0 .775.138 1.05.413.275.275.413.625.413 1.05 0 .425-.138.775-.413 1.05L20.1 18l7.35 7.35c.275.275.413.625.413 1.05 0 .425-.138.775-.413 1.05-.275.275-.625.413-1.05.413-.425 0-.775-.138-1.05-.413L18 20.1Z" fill="#585C5E" /></svg></span>
</button>
<div
ref="sidebarContainer"
:class="['sidebar', { 'sidebar--open': isOpen }]
"
role="navigation"
aria-label="Hauptnavigation"
:aria-hidden="(!isOpen).toString()"
>
<ul class="sidebar__main-menu">
<li class="sidebar__item">
<nuxt-link
:to="localePath('/settings/account')"
class="sidebar__link"
:tabindex="isOpen ? 0 : -1"
:aria-hidden="!isOpen"
>
<i class="fa-solid fa-user sidebar__icon" /><span class="sidebar__text">{{ t('Account') }}</span>
</nuxt-link>
</li>
<li v-if="dataStore.user.team_subscription_plan == '0' && dataStore.user.enterprise_subscription_plan == '0' && (dataStore.user.basic_subscription_plan == '1')" class="sidebar__item">
<nuxt-link :to="localePath('/settings/subscription')" class="sidebar__link" :tabindex="isOpen ? 0 : -1">
<i class="fa-solid fa-credit-card sidebar__icon" /><span class="sidebar__text">{{ t('Subscription') }}</span>
</nuxt-link>
</li>
<li class="sidebar__item">
<nuxt-link
:to="localePath('/settings/soundscape')"
class="sidebar__link"
:tabindex="isOpen ? 0 : -1"
:aria-hidden="!isOpen"
>
<i class="fa-solid fa-volume-high sidebar__icon" /><span class="sidebar__text">{{ t('Settings') }}</span>
</nuxt-link>
</li>
<li class="sidebar__item">
<nuxt-link
:to="localePath('/settings/about')"
class="sidebar__link"
:tabindex="isOpen ? 0 : -1"
:aria-hidden="!isOpen"
>
<i class="fa-solid fa-circle-info sidebar__icon" /><span class="sidebar__text"> {{ t('About Mindboost') }}</span>
</nuxt-link>
</li>
<li class="sidebar__item">
<nuxt-link
:to="localePath('/settings/faq')"
class="sidebar__link"
:tabindex="isOpen ? 0 : -1"
:aria-hidden="!isOpen"
>
<i class="fa-solid fa-circle-question sidebar__icon" /><span class="sidebar__text">{{ t('FAQ') }} </span>
</nuxt-link>
</li>
</ul>
<ul class="sidebar__secondary-menu">
<li class="sidebar__item">
<nuxt-link
:to="localePath('/settings/imprint')"
class="sidebar__link sidebar__link--secondary"
:tabindex="isOpen ? 0 : -1"
:aria-hidden="!isOpen"
>
<i class="fa-solid fa-stamp sidebar__icon" /><span class="sidebar__text">{{ t('Imprint') }} </span>
</nuxt-link>
</li>
<li class="sidebar__item">
<nuxt-link
:to="localePath('/settings/dataprotection')"
class="sidebar__link sidebar__link--secondary"
:tabindex="isOpen ? 0 : -1"
:aria-hidden="!isOpen"
>
<i class="fa-solid fa-shield sidebar__icon" /><span class="sidebar__text">{{ t('Data Protection') }}</span>
</nuxt-link>
</li>
<li class="sidebar__item">
<nuxt-link
:to="localePath('/settings/termsandcondition')"
class="sidebar__link sidebar__link--secondary"
:tabindex="isOpen ? 0 : -1"
:aria-hidden="!isOpen"
@blur="handleBlur"
>
<i class="fa-solid fa-scale-balanced sidebar__icon" /><span class="sidebar__text">{{ t('Terms & Conditions') }}</span>
</nuxt-link>
</li>
</ul>
</div>
</template>
<script>
import { onMounted, onBeforeUnmount, ref } from 'vue'
import { useUserStore } from '~/stores/user'
export default {
setup () {
const isOpen = ref(false)
const sidebarContainer = ref(null)
const dataStore = useUserStore()
const { t } = useI18n()
const localePath = useLocalePath()
const toggleSidebar = () => {
isOpen.value = !isOpen.value
}
const handleClickOutside = (event) => {
if (sidebarContainer.value && !sidebarContainer.value.contains(event.target)) {
isOpen.value = false
}
}
const handleBlur = () => {
// Timeout nötig, um neuen Fokus zu berücksichtigen
setTimeout(() => {
const active = document.activeElement
if (
isOpen.value &&
sidebarContainer.value &&
!sidebarContainer.value.contains(active)
) {
isOpen.value = false
}
}, 10)
}
const handleEscape = (e) => {
if (e.key === 'Escape' && isOpen.value) {
isOpen.value = false
document.querySelector('.sidebar__menu-icon')?.focus()
}
}
onMounted(() => {
document.addEventListener('click', handleClickOutside)
document.addEventListener('keydown', handleEscape)
})
onBeforeUnmount(() => {
document.removeEventListener('click', handleClickOutside)
document.removeEventListener('keydown', handleEscape)
})
return { t, localePath, dataStore, isOpen, toggleSidebar, sidebarContainer, handleBlur }
}
}
</script>
<style lang="scss">
.sidebar {
position: fixed;
top: 0;
right: -350px;
width: 350px;
height: 100%;
background-color: #fff;
transition: right 0.3s ease;
z-index: 1000;
padding: 6em 0em 2em;
display: flex;
flex-direction: column;
justify-content: space-between;
&--open {
box-shadow: -2px 0 10px rgba(0, 0, 0, 0.2);
right: 0;
}
&.sidebar--settings {
background-color: rgb(244, 245, 247);
position: unset;
width: 100%;
height: 100%;
padding-top: 0;
.sidebar__item {
&::after {
content: none;
}
}
}
&__menu-icon {
background: none;
border: none;
cursor: pointer;
z-index: 1001;
}
&__main-menu,
&__secondary-menu {
list-style: none;
padding: 0;
margin: 0;
width: 100%;
}
&__item {
position: relative;
&::after {
content: '';
position: absolute;
left: 1em;
right: 1em;
bottom: 0;
height: 2px;
background-color: #f3f3f3;
transition: 250ms ease-in-out;
}
&:hover {
&::after {
background-color: #e9c046;
}
}
&:last-child {
&::after {
content: none;
}
}
}
.sidebar__link {
text-decoration: none;
color: #585C5E;
font-size: 1.2em;
display: block;
width: 100%;
padding: 0.75em 1em;
font-weight: 700;
transition: 250ms ease-in-out;
&:hover {
background-color: #e9c046;
color: white;
}
&--secondary {
font-size: 0.9em;
color: #85878C;
&:hover {
background-color: transparent;
color: #85878C;
text-decoration: underline;
}
}
}
.sidebar__icon {
display: none;
}
&__secondary-menu {
.sidebar__item {
&::after{
content: none;
}
}
}
&.sidebar--settings {
.sidebar__icon {
@media only screen and (max-width: 576px) {
display: block;
}
}
}
}
</style>