vikunja/frontend/src/components/home/Navigation.vue

249 lines
5.2 KiB
Vue

<template>
<aside
:class="{'is-active': baseStore.menuActive, 'is-resizing': isResizing}"
class="menu-container"
:style="{'--sidebar-width': sidebarWidth}"
>
<nav class="menu top-menu">
<RouterLink
:to="{name: 'home'}"
class="logo"
:aria-label="$t('navigation.overview')"
>
<Logo
width="164"
height="48"
/>
</RouterLink>
<menu class="menu-list other-menu-items">
<li>
<RouterLink
v-shortcut="'KeyG KeyO'"
:to="{ name: 'home'}"
>
<span class="menu-item-icon icon">
<Icon icon="calendar" />
</span>
{{ $t('navigation.overview') }}
</RouterLink>
</li>
<li>
<RouterLink
v-shortcut="'KeyG KeyU'"
:to="{ name: 'tasks.range'}"
>
<span class="menu-item-icon icon">
<Icon :icon="['far', 'calendar-alt']" />
</span>
{{ $t('navigation.upcoming') }}
</RouterLink>
</li>
<li>
<RouterLink
v-shortcut="'KeyG KeyP'"
:to="{ name: 'projects.index'}"
>
<span class="menu-item-icon icon">
<Icon icon="layer-group" />
</span>
{{ $t('project.projects') }}
</RouterLink>
</li>
<li>
<RouterLink
v-shortcut="'KeyG KeyA'"
:to="{ name: 'labels.index'}"
>
<span class="menu-item-icon icon">
<Icon icon="tags" />
</span>
{{ $t('label.title') }}
</RouterLink>
</li>
<li>
<RouterLink
v-shortcut="'KeyG KeyM'"
:to="{ name: 'teams.index'}"
>
<span class="menu-item-icon icon">
<Icon icon="users" />
</span>
{{ $t('team.title') }}
</RouterLink>
</li>
<li>
<RouterLink
:to="{ name: 'projects.bin'}"
>
<span class="menu-item-icon icon">
<Icon icon="trash-alt" />
</span>
{{ $t('project.bin.title') }}
</RouterLink>
</li>
</menu>
</nav>
<Loading
v-if="projectStore.isLoading"
variant="small"
/>
<template v-else>
<nav
v-if="favoriteProjects.length"
class="menu"
>
<ProjectsNavigation
:model-value="favoriteProjects"
:can-edit-order="false"
:can-collapse="false"
/>
</nav>
<nav
v-if="savedFilterProjects.length"
class="menu"
>
<ProjectsNavigation
:model-value="savedFilterProjects"
:can-edit-order="false"
:can-collapse="false"
/>
</nav>
<nav class="menu">
<ProjectsNavigation
:model-value="projects"
:can-edit-order="true"
:can-collapse="true"
/>
</nav>
</template>
<PoweredByLink
class="mbs-auto"
utm-medium="navigation"
/>
<div
v-if="!isMobile"
class="resize-handle"
@mousedown="startResize"
@touchstart="startResize"
/>
</aside>
</template>
<script setup lang="ts">
import {computed} from 'vue'
import PoweredByLink from '@/components/home/PoweredByLink.vue'
import Logo from '@/components/home/Logo.vue'
import Loading from '@/components/misc/Loading.vue'
import {useBaseStore} from '@/stores/base'
import {useProjectStore} from '@/stores/projects'
import ProjectsNavigation from '@/components/home/ProjectsNavigation.vue'
import type {IProject} from '@/modelTypes/IProject'
import {useSidebarResize} from '@/composables/useSidebarResize'
const baseStore = useBaseStore()
const projectStore = useProjectStore()
const {sidebarWidth, isResizing, startResize, isMobile} = useSidebarResize()
// Cast readonly arrays to mutable type - the arrays are not actually mutated by the component
const projects = computed(() => projectStore.notArchivedRootProjects as IProject[])
const favoriteProjects = computed(() => projectStore.favoriteProjects as IProject[])
const savedFilterProjects = computed(() => projectStore.savedFilterProjects as IProject[])
</script>
<style lang="scss" scoped>
.logo {
display: block;
padding-inline-start: 1rem;
margin-inline-end: 1rem;
margin-block-end: 1rem;
@media screen and (min-width: $tablet) {
display: none;
}
}
.menu-container {
--sidebar-width: #{$navbar-width};
display: flex;
flex-direction: column;
background: var(--site-background);
color: $vikunja-nav-color;
padding: 1rem 0;
transition: transform $transition-duration ease-in;
position: fixed;
inset-block-start: $navbar-height;
inset-block-end: 0;
inset-inline-start: 0;
transform: translateX(-100%);
inline-size: var(--sidebar-width);
overflow-y: auto;
[dir="rtl"] & {
transform: translateX(100%);
}
@media screen and (max-width: $tablet) {
inset-block-start: 0;
inline-size: 70vw;
z-index: 20;
}
&.is-active {
transform: translateX(0);
transition: transform $transition-duration ease-out;
}
&.is-resizing {
transition: none;
}
}
.resize-handle {
position: absolute;
inset-block-start: 0;
inset-block-end: 0;
inset-inline-end: 0;
inline-size: 4px;
cursor: ew-resize;
background: transparent;
transition: background-color $transition-duration ease;
touch-action: none;
&:hover,
&:active {
background-color: var(--primary);
}
}
.top-menu .menu-list {
li {
font-weight: 600;
font-family: $vikunja-font;
}
.list-menu-link,
li > a {
padding-inline-start: 2rem;
display: inline-block;
.icon {
padding-block-end: .25rem;
}
}
}
.menu + .menu {
padding-block-start: math.div($navbar-padding, 2);
}
</style>