fix: sidebar menu collapse/expand on mobile

This commit is contained in:
Phan An 2024-08-18 17:06:06 +02:00
parent b994f2dc94
commit bb47d5fb3b
4 changed files with 53 additions and 39 deletions

View file

@ -10,7 +10,9 @@
py-0 px-6 h-k-header-height" py-0 px-6 h-k-header-height"
> >
<div class="btn-group"> <div class="btn-group">
<SidebarMenuToggleButton /> <ExtraDrawerButton @click.prevent="expandSidebar">
<Icon :icon="faBars" fixed-width />
</ExtraDrawerButton>
<ExtraDrawerTabHeader v-if="songPlaying" v-model="activeTab" /> <ExtraDrawerTabHeader v-if="songPlaying" v-model="activeTab" />
</div> </div>
@ -70,16 +72,17 @@
<script lang="ts" setup> <script lang="ts" setup>
import isMobile from 'ismobilejs' import isMobile from 'ismobilejs'
import { faBars } from '@fortawesome/free-solid-svg-icons'
import { computed, defineAsyncComponent, onMounted, ref, Ref, watch } from 'vue' import { computed, defineAsyncComponent, onMounted, ref, Ref, watch } from 'vue'
import { albumStore, artistStore, preferenceStore } from '@/stores' import { albumStore, artistStore, preferenceStore } from '@/stores'
import { useErrorHandler, useThirdPartyServices } from '@/composables' import { useErrorHandler, useThirdPartyServices } from '@/composables'
import { isSong, requireInjection } from '@/utils' import { eventBus, isSong, requireInjection } from '@/utils'
import { CurrentPlayableKey } from '@/symbols' import { CurrentPlayableKey } from '@/symbols'
import ProfileAvatar from '@/components/ui/ProfileAvatar.vue' import ProfileAvatar from '@/components/ui/ProfileAvatar.vue'
import SidebarMenuToggleButton from '@/components/ui/SidebarMenuToggleButton.vue'
import AboutKoelButton from '@/components/layout/main-wrapper/extra-drawer/AboutKoelButton.vue' import AboutKoelButton from '@/components/layout/main-wrapper/extra-drawer/AboutKoelButton.vue'
import LogoutButton from '@/components/layout/main-wrapper/extra-drawer/LogoutButton.vue' import LogoutButton from '@/components/layout/main-wrapper/extra-drawer/LogoutButton.vue'
import ExtraDrawerButton from '@/components/layout/main-wrapper/extra-drawer/ExtraDrawerButton.vue'
const LyricsPane = defineAsyncComponent(() => import('@/components/ui/LyricsPane.vue')) const LyricsPane = defineAsyncComponent(() => import('@/components/ui/LyricsPane.vue'))
const ArtistInfo = defineAsyncComponent(() => import('@/components/artist/ArtistInfo.vue')) const ArtistInfo = defineAsyncComponent(() => import('@/components/artist/ArtistInfo.vue'))
@ -119,6 +122,7 @@ watch(playable, song => {
watch(activeTab, tab => (preferenceStore.active_extra_panel_tab = tab)) watch(activeTab, tab => (preferenceStore.active_extra_panel_tab = tab))
const onProfileLinkClick = () => isMobile.any && (activeTab.value = null) const onProfileLinkClick = () => isMobile.any && (activeTab.value = null)
const expandSidebar = () => eventBus.emit('TOGGLE_SIDEBAR')
onMounted(() => isMobile.any || (activeTab.value = preferenceStore.active_extra_panel_tab)) onMounted(() => isMobile.any || (activeTab.value = preferenceStore.active_extra_panel_tab))
</script> </script>

View file

@ -3,7 +3,7 @@
exports[`renders without a current song 1`] = ` exports[`renders without a current song 1`] = `
<aside data-v-847f0b60="" class="fixed sm:relative top-0 w-screen md:w-auto flex flex-col md:flex-row-reverse z-[2] text-k-text-secondary"> <aside data-v-847f0b60="" class="fixed sm:relative top-0 w-screen md:w-auto flex flex-col md:flex-row-reverse z-[2] text-k-text-secondary">
<div data-v-847f0b60="" class="controls flex md:flex-col justify-between items-center md:w-[64px] md:py-6 tw:px-0 bg-black/5 md:border-l border-solid md:border-l-white/5 md:border-b-0 md:shadow-none z-[2] w-screen flex-row border-b border-b-white/5 border-l-0 shadow-xl py-0 px-6 h-k-header-height"> <div data-v-847f0b60="" class="controls flex md:flex-col justify-between items-center md:w-[64px] md:py-6 tw:px-0 bg-black/5 md:border-l border-solid md:border-l-white/5 md:border-b-0 md:shadow-none z-[2] w-screen flex-row border-b border-b-white/5 border-l-0 shadow-xl py-0 px-6 h-k-header-height">
<div data-v-847f0b60="" class="btn-group"><button data-v-5e31283f="" data-v-847f0b60="" class="relative flex items-center justify-center h-[42px] aspect-square rounded-full bg-none md:bg-black/30 text-xl opacity-70 transition-opacity duration-200 ease-in-out text-current cursor-pointer hover:active-state active:scale-90 block md:hidden" type="button"><br data-testid="Icon" icon="[object Object]" fixed-width=""></button> <div data-v-847f0b60="" class="btn-group"><button data-v-5e31283f="" data-v-847f0b60="" class="relative flex items-center justify-center h-[42px] aspect-square rounded-full bg-none md:bg-black/30 text-xl opacity-70 transition-opacity duration-200 ease-in-out text-current cursor-pointer hover:active-state active:scale-90" type="button"><br data-v-847f0b60="" data-testid="Icon" icon="[object Object]" fixed-width=""></button>
<!--v-if--> <!--v-if-->
</div> </div>
<div data-v-847f0b60="" class="btn-group"><button data-v-5e31283f="" data-v-847f0b60="" class="relative flex items-center justify-center h-[42px] aspect-square rounded-full bg-none md:bg-black/30 text-xl opacity-70 transition-opacity duration-200 ease-in-out text-current cursor-pointer hover:active-state active:scale-90" type="button" title="About Koel"><br data-testid="Icon" icon="[object Object]"> <div data-v-847f0b60="" class="btn-group"><button data-v-5e31283f="" data-v-847f0b60="" class="relative flex items-center justify-center h-[42px] aspect-square rounded-full bg-none md:bg-black/30 text-xl opacity-70 transition-opacity duration-200 ease-in-out text-current cursor-pointer hover:active-state active:scale-90" type="button" title="About Koel"><br data-testid="Icon" icon="[object Object]">

View file

@ -1,45 +1,55 @@
<template> <template>
<nav <nav
:class="{ collapsed: !expanded, 'tmp-showing': tmpShowing, showing: mobileShowing }" :class="{ collapsed: !expanded, 'tmp-showing': tmpShowing, showing: mobileShowing }"
class="group flex flex-col fixed md:relative w-full md:w-k-sidebar-width z-10" class="group left-0 top-0 flex flex-col fixed h-full w-full md:relative md:w-k-sidebar-width z-[999] md:z-10"
@mouseenter="onMouseEnter" @mouseenter="onMouseEnter"
@mouseleave="onMouseLeave" @mouseleave="onMouseLeave"
> >
<section class="home-search-block p-6 flex gap-2"> <section class="btn-collapse-block flex md:hidden items-center border-b border-b-white/5 h-k-header-height px-6">
<HomeButton /> <div class="bg-white/5 rounded-full">
<SearchForm class="flex-1" /> <ExtraDrawerButton @click.prevent="collapseSidebar">
</section> <Icon :icon="faTimes" fixed-width />
</ExtraDrawerButton>
</div>
</section>
<section v-koel-overflow-fade class="pt-2 pb-10 overflow-y-auto space-y-8"> <section class="home-search-block p-6 flex gap-2">
<SidebarYourMusicSection /> <HomeButton />
<SidebarPlaylistsSection /> <SearchForm class="flex-1" />
<SidebarManageSection v-if="showManageSection" /> </section>
</section>
<section v-if="!isPlus && isAdmin" class="p-6 flex-1 flex flex-col-reverse"> <section v-koel-overflow-fade class="pt-2 pb-10 overflow-y-auto space-y-8">
<BtnUpgradeToPlus /> <SidebarYourMusicSection />
</section> <SidebarPlaylistsSection />
<SidebarManageSection v-if="showManageSection" />
</section>
<SidebarToggleButton <section v-if="!isPlus && isAdmin" class="p-6 flex-1 flex flex-col-reverse">
class="opacity-0 no-hover:hidden group-hover:opacity-100 transition" <BtnUpgradeToPlus />
v-model="expanded" </section>
:class="expanded || 'opacity-100'"
/> <SidebarToggleButton
</nav> class="opacity-0 no-hover:hidden group-hover:opacity-100 transition"
v-model="expanded"
:class="expanded || 'opacity-100'"
/>
</nav>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { faTimes } from '@fortawesome/free-solid-svg-icons'
import { computed, ref, watch } from 'vue' import { computed, ref, watch } from 'vue'
import { eventBus } from '@/utils' import { eventBus } from '@/utils'
import { useAuthorization, useKoelPlus, useLocalStorage, useRouter, useUpload } from '@/composables' import { useAuthorization, useKoelPlus, useLocalStorage, useRouter, useUpload } from '@/composables'
import SidebarPlaylistsSection from './SidebarPlaylistsSection.vue'
import SearchForm from '@/components/ui/SearchForm.vue'
import BtnUpgradeToPlus from '@/components/koel-plus/BtnUpgradeToPlus.vue' import BtnUpgradeToPlus from '@/components/koel-plus/BtnUpgradeToPlus.vue'
import SidebarYourMusicSection from './SidebarYourLibrarySection.vue' import ExtraDrawerButton from '@/components/layout/main-wrapper/extra-drawer/ExtraDrawerButton.vue'
import SidebarManageSection from './SidebarManageSection.vue'
import SidebarToggleButton from '@/components/layout/main-wrapper/sidebar/SidebarToggleButton.vue'
import HomeButton from '@/components/layout/main-wrapper/sidebar/HomeButton.vue' import HomeButton from '@/components/layout/main-wrapper/sidebar/HomeButton.vue'
import SearchForm from '@/components/ui/SearchForm.vue'
import SidebarManageSection from './SidebarManageSection.vue'
import SidebarPlaylistsSection from './SidebarPlaylistsSection.vue'
import SidebarToggleButton from '@/components/layout/main-wrapper/sidebar/SidebarToggleButton.vue'
import SidebarYourMusicSection from './SidebarYourLibrarySection.vue'
const { onRouteChanged } = useRouter() const { onRouteChanged } = useRouter()
const { isAdmin } = useAuthorization() const { isAdmin } = useAuthorization()
@ -81,6 +91,8 @@ const onMouseLeave = (e: MouseEvent) => {
onRouteChanged(_ => (mobileShowing.value = false)) onRouteChanged(_ => (mobileShowing.value = false))
const collapseSidebar = () => (mobileShowing.value = false)
/** /**
* Listen to toggle sidebar event to show or hide the sidebar. * Listen to toggle sidebar event to show or hide the sidebar.
* This should only be triggered on a mobile device. * This should only be triggered on a mobile device.
@ -104,9 +116,9 @@ nav {
} }
&.tmp-showing { &.tmp-showing {
@apply absolute h-screen z-50 bg-k-bg-primary w-k-sidebar-width shadow-2xl; @apply fixed h-screen bg-k-bg-primary w-k-sidebar-width shadow-2xl z-[999];
> *:not(.btn-toggle) { > *:not(.btn-toggle, .btn-collapse-block) {
@apply block; @apply block;
} }
@ -118,11 +130,9 @@ nav {
@media screen and (max-width: 768px) { @media screen and (max-width: 768px) {
@mixin themed-background; @mixin themed-background;
z-index: 999;
transform: translateX(-100vw); transform: translateX(-100vw);
transition: transform .2s ease-in-out; transition: transform .2s ease-in-out;
height: calc(100vh - var(--header-height));
&.showing { &.showing {
transform: translateX(0); transform: translateX(0);

View file

@ -20,8 +20,8 @@ new class extends UnitTestCase {
expect(screen.getByTestId('sidebar-item').classList.contains('current')).toBe(true) expect(screen.getByTestId('sidebar-item').classList.contains('current')).toBe(true)
}) })
it ('emits the sidebar toggle event when clicked', async () => { it('emits the sidebar toggle event when clicked', async () => {
const mock = this.mock(eventBus, 'emit') const mock = this.mock(eventBus, 'emit')
this.renderComponent() this.renderComponent()
await this.user.click(screen.getByTestId('sidebar-item')) await this.user.click(screen.getByTestId('sidebar-item'))