add animation to the mobile sidebar

This commit is contained in:
Gregor Vostrak
2026-02-12 13:51:53 +01:00
parent b258717211
commit 052424a581
4 changed files with 74 additions and 35 deletions

View File

@@ -1,5 +1,10 @@
<script setup lang="ts">
import { ChartBarIcon, ArrowDownTrayIcon, EllipsisVerticalIcon, LockClosedIcon } from '@heroicons/vue/20/solid';
import {
ChartBarIcon,
ArrowDownTrayIcon,
EllipsisVerticalIcon,
LockClosedIcon,
} from '@heroicons/vue/20/solid';
import { SaveIcon } from 'lucide-vue-next';
import { getOrganizationCurrencyString } from '@/utils/money';
import {
@@ -281,9 +286,7 @@ const tableData = computed(() => {
class="h-14 sm:h-16 border-b border-default-background-separator flex flex-wrap gap-y-3 justify-between items-center">
<div class="flex items-center space-x-3 sm:space-x-6">
<PageTitle :icon="ChartBarIcon" title="Reporting"></PageTitle>
<ReportingTabNavbar
active="reporting"
class="hidden sm:flex"></ReportingTabNavbar>
<ReportingTabNavbar active="reporting" class="hidden sm:flex"></ReportingTabNavbar>
</div>
<div class="hidden sm:flex space-x-2">
<DropdownMenu>
@@ -312,10 +315,7 @@ const tableData = computed(() => {
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<SecondaryButton
v-if="canCreateReports()"
:icon="SaveIcon"
@click="onSaveReportClick">
<SecondaryButton v-if="canCreateReports()" :icon="SaveIcon" @click="onSaveReportClick">
Save Report
</SecondaryButton>
</div>
@@ -339,15 +339,9 @@ const tableData = computed(() => {
<DropdownMenuItem @click="triggerExport('xlsx')">
Export as Excel
</DropdownMenuItem>
<DropdownMenuItem @click="triggerExport('csv')">
Export as CSV
</DropdownMenuItem>
<DropdownMenuItem @click="triggerExport('ods')">
Export as ODS
</DropdownMenuItem>
<DropdownMenuItem
v-if="canCreateReports()"
@click="onSaveReportClick">
<DropdownMenuItem @click="triggerExport('csv')"> Export as CSV </DropdownMenuItem>
<DropdownMenuItem @click="triggerExport('ods')"> Export as ODS </DropdownMenuItem>
<DropdownMenuItem v-if="canCreateReports()" @click="onSaveReportClick">
Save Report
</DropdownMenuItem>
</DropdownMenuContent>

View File

@@ -4,7 +4,6 @@ import Banner from '@/Components/Banner.vue';
import OrganizationSwitcher from '@/Components/OrganizationSwitcher.vue';
import CurrentSidebarTimer from '@/Components/CurrentSidebarTimer.vue';
import {
Bars3Icon,
CalendarIcon,
ChartBarIcon,
ClockIcon,
@@ -18,11 +17,13 @@ import {
UserGroupIcon,
XMarkIcon,
DocumentTextIcon,
ViewColumnsIcon,
} from '@heroicons/vue/20/solid';
import { PanelLeft } from 'lucide-vue-next';
import NavigationSidebarItem from '@/Components/NavigationSidebarItem.vue';
import UserSettingsIcon from '@/Components/UserSettingsIcon.vue';
import MainContainer from '@/packages/ui/src/MainContainer.vue';
import { onMounted, provide, ref } from 'vue';
import { nextTick, onMounted, provide, ref } from 'vue';
import NotificationContainer from '@/Components/NotificationContainer.vue';
import { initializeStores } from '@/utils/init';
import { useCurrentTimeEntryStore } from '@/utils/useCurrentTimeEntry';
@@ -35,6 +36,7 @@ import {
canViewProjects,
canViewReport,
canViewTags,
canViewTasks,
} from '@/utils/permissions';
import { isBillingActivated, isInvoicingActivated } from '@/utils/billing';
import type { User } from '@/types/models';
@@ -61,6 +63,24 @@ defineProps({
});
const showSidebarMenu = ref(false);
const sidebarVisible = ref(false);
function openSidebar() {
showSidebarMenu.value = true;
nextTick(() => {
requestAnimationFrame(() => {
sidebarVisible.value = true;
});
});
}
function closeSidebar() {
sidebarVisible.value = false;
setTimeout(() => {
showSidebarMenu.value = false;
}, 200);
}
const isUnloading = ref(false);
const { organization, isLoading: isOrganizationLoading } = useOrganizationQuery(
@@ -102,11 +122,26 @@ const page = usePage<{
<template>
<div v-bind="$attrs" class="flex flex-wrap bg-background text-text-secondary">
<!-- Mobile sidebar overlay -->
<Teleport to="body">
<div
v-if="showSidebarMenu"
class="fixed inset-0 z-40 lg:hidden"
@click="closeSidebar">
<div
class="absolute inset-0 bg-default-background transition-opacity duration-200"
:class="sidebarVisible ? 'opacity-50' : 'opacity-0'" />
</div>
</Teleport>
<div
:class="{
'!flex bg-default-background w-full z-30': showSidebarMenu,
}"
class="flex-shrink-0 h-screen hidden fixed w-[230px] 2xl:w-[250px] px-2.5 2xl:px-3 py-4 lg:flex flex-col justify-between">
:class="[
sidebarVisible
? 'max-lg:translate-x-0 max-lg:shadow-xl'
: 'max-lg:-translate-x-full',
]"
class="flex-shrink-0 h-screen fixed w-[280px] px-2.5 py-4 hidden lg:flex flex-col justify-between bg-background border-r border-default-background-separator max-lg:z-50 max-lg:transition-transform max-lg:duration-200 max-lg:ease-in-out lg:w-[230px] 2xl:w-[250px] 2xl:px-3 lg:border-r-0"
:style="showSidebarMenu ? { display: 'flex' } : undefined">
<div class="flex flex-col h-full">
<div
class="border-b border-default-background-separator pb-2 flex items-center gap-1">
@@ -121,9 +156,13 @@ const page = usePage<{
@click="openPalette">
<MagnifyingGlassIcon class="h-4 w-4 text-icon-default" />
</Button>
<XMarkIcon
class="w-8 lg:hidden flex-shrink-0"
@click="showSidebarMenu = false"></XMarkIcon>
<Button
variant="ghost"
size="icon"
class="h-7 w-7 flex-shrink-0 lg:hidden"
@click="closeSidebar">
<XMarkIcon class="h-4 w-4 text-icon-default" />
</Button>
</div>
<div class="border-b border-default-background-separator">
<CurrentSidebarTimer></CurrentSidebarTimer>
@@ -151,6 +190,12 @@ const page = usePage<{
:icon="CalendarIcon"
:current="route().current('calendar')"
:href="route('calendar')"></NavigationSidebarItem>
<NavigationSidebarItem
v-if="canViewTasks()"
title="Board"
:icon="ViewColumnsIcon"
:href="route('kanban')"
:current="route().current('kanban')"></NavigationSidebarItem>
<NavigationSidebarItem
title="Reporting"
:icon="ChartBarIcon"
@@ -279,9 +324,13 @@ const page = usePage<{
class="h-screen overflow-y-auto flex flex-col bg-default-background border-l border-default-background-separator">
<div
class="lg:hidden w-full px-3 py-1 border-b border-b-default-background-separator text-text-secondary flex justify-between items-center">
<Bars3Icon
class="w-7 text-text-secondary"
@click="showSidebarMenu = !showSidebarMenu"></Bars3Icon>
<Button
variant="ghost"
size="icon"
class="h-7 w-7 shrink-0"
@click="openSidebar">
<PanelLeft class="h-4 w-4 text-icon-default" />
</Button>
<div class="flex items-center gap-1">
<OrganizationSwitcher></OrganizationSwitcher>
<Button

View File

@@ -266,9 +266,7 @@ async function downloadExport(format: ExportFormat) {
class="h-14 sm:h-16 border-b border-default-background-separator flex flex-wrap gap-y-3 justify-between items-center">
<div class="flex items-center space-x-3 sm:space-x-6">
<PageTitle :icon="ChartBarIcon" title="Reporting"></PageTitle>
<ReportingTabNavbar
active="detailed"
class="hidden sm:flex"></ReportingTabNavbar>
<ReportingTabNavbar active="detailed" class="hidden sm:flex"></ReportingTabNavbar>
</div>
<div class="hidden sm:block">
<DropdownMenu>

View File

@@ -72,9 +72,7 @@ watch(currentPage, () => {
class="h-14 sm:h-16 border-b border-default-background-separator flex flex-wrap gap-y-3 justify-between items-center">
<div class="flex items-center space-x-3 sm:space-x-6">
<PageTitle :icon="ChartBarIcon" title="Reporting"></PageTitle>
<ReportingTabNavbar
active="shared"
class="hidden sm:flex"></ReportingTabNavbar>
<ReportingTabNavbar active="shared" class="hidden sm:flex"></ReportingTabNavbar>
</div>
</MainContainer>
<MainContainer class="sm:hidden py-2 border-b border-default-background-separator">