dml_frontend/components/Breadcrumbs.vue

262 lines
6.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<nav
v-if="breadcrumbs.length"
class="breadcrumbs"
:class="{ expanded: isOpen }"
aria-label="Breadcrumb"
>
<div class="breadcrumb-container">
<!-- 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"
aria-hidden="true"
focusable="false"
>
<title>Startseite</title>
<path
d="M575.8 255.5c0 18-15 32.1-32 32.1l-32 0 .7 160.2c0 2.7-.2 5.4-.5 8.1l0 16.2c0 22.1-17.9 40-40 40l-16 0c-1.1 0-2.2 0-3.3-.1c-1.4 .1-2.8 .1-4.2 .1L416 512l-24 0c-22.1 0-40-17.9-40-40l0-24 0-64c0-17.7-14.3-32-32-32l-64 0c-17.7 0-32 14.3-32 32l0 64 0 24c0 22.1-17.9 40-40 40l-24 0-31.9 0c-1.5 0-3-.1-4.5-.2c-1.2 .1-2.4 .2-3.6 .2l-16 0c-22.1 0-40-17.9-40-40l0-112c0-.9 0-1.9 .1-2.8l0-69.7-32 0c-18 0-32-14-32-32.1c0-9 3-17 10-24L266.4 8c7-7 15-8 22-8s15 2 21 7L564.8 231.5c8 7 12 15 11 24z"
/>
</svg>
</div>
<!-- Breadcrumbs -->
<ul :class="{ open: isOpen, closed: !isOpen }">
<li
v-for="(crumb, index) in breadcrumbs"
:key="index"
>
<router-link
v-if="index < breadcrumbs.length - 1"
:to="crumb.to"
:title="crumb.labelFull"
>
{{ crumb.label }}
</router-link>
<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>
</ul>
</div>
</nav>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRoute } from 'vue-router'
import { i18nPages } from '~/i18n/i18n-pages'
interface Breadcrumb {
label: string
labelFull: string
to: string
}
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
if (label.length > 25) {
label = label.slice(0, 22) + '...'
}
return { label, labelFull }
}
function buildUrl(loc: string, path: string): string {
return loc === 'de' ? '/' + path.replace(/^\//, '') : `/${loc}/${path.replace(/^\//, '')}`
}
const breadcrumbs = computed<Breadcrumb[]>(() => {
const loc = locale.value
const pathWithoutLang = route.path.replace(`/${loc}`, '')
const segments = pathWithoutLang.split('/').filter(Boolean)
if (segments.length === 2 && segments[0] === 'projekt') {
const referencesPath = i18nPages.references?.[loc] || '/references'
const referencesLabel = t('references') || 'References'
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'
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) }
})
})
</script>
<style lang="sass">
.breadcrumbs
position: fixed
top: 12vh
left: -12px
background-color: rgba(white, .98)
border: 1px solid darken($lightgrey, 5%)
z-index: 22
padding: .2rem 0
border-top-right-radius: .8rem
border-bottom-right-radius: .8rem
transition: all 0.4s ease
.breadcrumb-container
display: flex
align-items: center
gap: 0.5rem
.breadcrumb-trigger
cursor: pointer
display: flex
align-items: center
justify-content: center
width: 1.8rem
height: 1.8rem
background: none
border: none
margin: 0 0 0 .5rem
svg
width: 1rem !important
height: 1rem !important
display: inline-block
path
fill: darken($lightgrey, 30%) !important
&:hover path
fill: darken($lightgrey, 30%)
// Animierbarer Breadcrumb-Block
ul
display: flex
flex-wrap: wrap
gap: 0.5rem
list-style: none
margin: 0
padding: 0
overflow: hidden
max-width: 0
opacity: 0
transition: all 0.4s ease
&.open
max-width: 500px
opacity: 1
&.closed
max-height: 0
opacity: 0
li
font-size: 1.1rem
display: flex
padding: 0.1rem .3rem
align-items: center
color: $darkgrey
a
color: darken($primaryColor, 10%)
.between
margin-right: 1.5rem
font-size: 1.5rem
font-family: 'Mainfont-Bold'
.close-btn
cursor: pointer
font-size: 1.2rem
font-weight: bold
background-color: darken($beige, 10%)
color: white
width: 1.5rem
height: 1.5rem
border-radius: 50%
position: relative
margin: .2rem .5rem 0 1rem
border: none
text-transform: none
&::before
position: absolute
content: '<'
color: white
top: 50%
left: 50%
transform: translate(-50%, -50%)
&:hover
color: darken($primaryColor, 15%)
a
color: #007BFF
text-decoration: none
&:hover
transform: scale(1.2)
span
font-weight: bold
</style>