181 lines
3.7 KiB
Vue
181 lines
3.7 KiB
Vue
<template>
|
|
<header class="app-header">
|
|
<div class="container">
|
|
<div class="header-content">
|
|
<button
|
|
class="menu-toggle lg:d-none"
|
|
@click="navigationStore.toggleMenu()"
|
|
:aria-label="navigationStore.isMenuOpen ? 'Close menu' : 'Open menu'"
|
|
aria-controls="navigation-menu"
|
|
>
|
|
<Menu v-if="!navigationStore.isMenuOpen" :size="24" />
|
|
<X v-else :size="24" />
|
|
</button>
|
|
|
|
<router-link to="/" class="logo-link">
|
|
<h1 class="logo">{{ hackerspaceStore.hackerspace.name }}</h1>
|
|
</router-link>
|
|
|
|
<nav class="header-nav d-none lg:d-flex">
|
|
<router-link
|
|
v-for="item in mainNavItems"
|
|
:key="item.id"
|
|
:to="item.path"
|
|
class="nav-link"
|
|
:class="{ active: $route.path === item.path }"
|
|
>
|
|
{{ item.title }}
|
|
</router-link>
|
|
</nav>
|
|
|
|
<div class="header-actions">
|
|
<button
|
|
class="icon-button"
|
|
@click="toggleTheme"
|
|
aria-label="Toggle theme"
|
|
>
|
|
<Sun v-if="isDark" :size="20" />
|
|
<Moon v-else :size="20" />
|
|
</button>
|
|
<button
|
|
class="icon-button"
|
|
@click="printPage"
|
|
aria-label="Print page"
|
|
>
|
|
<Printer :size="20" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, ref } from 'vue'
|
|
import { useRoute } from 'vue-router'
|
|
import { useNavigationStore } from '@/stores/navigation'
|
|
import { useHackerspaceStore } from '@/stores/hackerspace'
|
|
import { Menu, X, Sun, Moon, Printer } from 'lucide-vue-next'
|
|
|
|
const route = useRoute()
|
|
const navigationStore = useNavigationStore()
|
|
const hackerspaceStore = useHackerspaceStore()
|
|
|
|
const isDark = ref(false)
|
|
|
|
const mainNavItems = computed(() =>
|
|
navigationStore.navItems.slice(0, 5)
|
|
)
|
|
|
|
const toggleTheme = () => {
|
|
isDark.value = !isDark.value
|
|
document.documentElement.classList.toggle('dark-theme')
|
|
}
|
|
|
|
const printPage = () => {
|
|
window.print()
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.app-header {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 100;
|
|
background-color: $color-paper;
|
|
border-bottom: 1px solid $color-border;
|
|
height: $header-height;
|
|
}
|
|
|
|
.header-content {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
height: $header-height;
|
|
gap: $space-4;
|
|
}
|
|
|
|
.menu-toggle {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: $radius-md;
|
|
transition: background-color $transition-fast;
|
|
|
|
&:hover {
|
|
background-color: $color-paper-light;
|
|
}
|
|
}
|
|
|
|
.logo-link {
|
|
text-decoration: none;
|
|
color: inherit;
|
|
|
|
&:hover {
|
|
text-decoration: none;
|
|
}
|
|
}
|
|
|
|
.logo {
|
|
font-size: $font-size-lg;
|
|
font-weight: 700;
|
|
margin: 0;
|
|
color: $color-text;
|
|
}
|
|
|
|
.header-nav {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: $space-2;
|
|
flex-grow: 1;
|
|
justify-content: center;
|
|
}
|
|
|
|
.nav-link {
|
|
padding: $space-2 $space-3;
|
|
border-radius: $radius-md;
|
|
font-size: $font-size-sm;
|
|
font-weight: 500;
|
|
transition: all $transition-fast;
|
|
|
|
&:hover {
|
|
background-color: $color-paper-light;
|
|
text-decoration: none;
|
|
}
|
|
|
|
&.active {
|
|
background-color: $color-paper-light;
|
|
color: $color-accent;
|
|
}
|
|
}
|
|
|
|
.header-actions {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: $space-2;
|
|
}
|
|
|
|
.icon-button {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: $radius-md;
|
|
transition: background-color $transition-fast;
|
|
color: $color-text-muted;
|
|
|
|
&:hover {
|
|
background-color: $color-paper-light;
|
|
color: $color-text;
|
|
}
|
|
}
|
|
|
|
@media (max-width: $breakpoint-lg) {
|
|
.header-nav {
|
|
display: none;
|
|
}
|
|
}
|
|
</style> |