audiobookshelf/client/components/app/ConfigSideNav.vue
Lars Kiesow 452af43916
Fix Hidden Settings Menu
This patch fixes several problems of the settings menu related to
display on mobile devices or small(ish) windows:

- The `isMobileLandscape` is now calculated correctly. Previously, this
  was set to `true` if a device was in portrait mode.

- Showing the button to collapse the settings menu and making the menu
  collapsible now use the same mechanism. Previously, it could happen
  that the menu was opened and not fixed, but no button to close it
  again was shown.

- The icons fore opening and closing the settings menu are now both
  arrows, indicating that their functionality is reversed.

- The button to open the menu now always has the string “Settings”,
  instead of using the name of the current page. The current page hader
  is listed below that anyway and this is the action component to open
  the settings menu after all.

This fixes #1334
2023-01-01 19:49:43 +01:00

168 lines
5.0 KiB
Vue

<template>
<div class="w-44 fixed left-0 top-16 h-full bg-bg bg-opacity-100 md:bg-opacity-70 shadow-lg border-r border-white border-opacity-5 py-3 transform transition-transform" :class="wrapperClass" v-click-outside="clickOutside">
<div v-show="isMobilePortrait" class="flex items-center justify-end pb-2 px-4 mb-1" @click="closeDrawer">
<span class="material-icons text-2xl">arrow_back</span>
</div>
<nuxt-link v-for="route in configRoutes" :key="route.id" :to="route.path" class="w-full px-4 h-12 border-b border-primary border-opacity-30 flex items-center cursor-pointer relative" :class="routeName === route.id ? 'bg-primary bg-opacity-70' : 'hover:bg-primary hover:bg-opacity-30'">
<p>{{ route.title }}</p>
<div v-show="routeName === route.iod" class="h-full w-0.5 bg-yellow-400 absolute top-0 left-0" />
</nuxt-link>
<div class="w-full h-12 px-4 border-t border-black border-opacity-20 absolute left-0 flex flex-col justify-center" :style="{ bottom: streamLibraryItem && isMobileLandscape ? '300px' : '65px' }">
<div class="flex justify-between">
<p class="underline font-mono text-sm" @click="clickChangelog">v{{ $config.version }}</p>
<p class="font-mono text-xs text-gray-300 italic">{{ Source }}</p>
</div>
<a v-if="hasUpdate" :href="githubTagUrl" target="_blank" class="text-warning text-xs">Latest: {{ latestVersion }}</a>
</div>
<modals-changelog-view-modal v-model="showChangelogModal" :changelog="currentVersionChangelog" :currentVersion="$config.version" />
</div>
</template>
<script>
export default {
props: {
isOpen: Boolean
},
data() {
return {
showChangelogModal: false
}
},
computed: {
Source() {
return this.$store.state.Source
},
currentLibraryId() {
return this.$store.state.libraries.currentLibraryId
},
userIsAdminOrUp() {
return this.$store.getters['user/getIsAdminOrUp']
},
configRoutes() {
if (!this.userIsAdminOrUp) {
return [
{
id: 'config-stats',
title: this.$strings.HeaderYourStats,
path: '/config/stats'
}
]
}
const configRoutes = [
{
id: 'config',
title: this.$strings.HeaderSettings,
path: '/config'
},
{
id: 'config-libraries',
title: this.$strings.HeaderLibraries,
path: '/config/libraries'
},
{
id: 'config-users',
title: this.$strings.HeaderUsers,
path: '/config/users'
},
{
id: 'config-sessions',
title: this.$strings.HeaderListeningSessions,
path: '/config/sessions'
},
{
id: 'config-backups',
title: this.$strings.HeaderBackups,
path: '/config/backups'
},
{
id: 'config-log',
title: this.$strings.HeaderLogs,
path: '/config/log'
},
{
id: 'config-notifications',
title: this.$strings.HeaderNotifications,
path: '/config/notifications'
},
{
id: 'config-item-metadata-utils',
title: this.$strings.HeaderItemMetadataUtils,
path: '/config/item-metadata-utils'
}
]
if (this.currentLibraryId) {
configRoutes.push({
id: 'config-library-stats',
title: this.$strings.HeaderLibraryStats,
path: '/config/library-stats'
})
configRoutes.push({
id: 'config-stats',
title: this.$strings.HeaderYourStats,
path: '/config/stats'
})
}
return configRoutes
},
wrapperClass() {
var classes = []
if (this.drawerOpen) classes.push('translate-x-0')
else classes.push('-translate-x-44')
if (this.isMobilePortrait) classes.push('z-50')
else classes.push('z-40')
return classes.join(' ')
},
isMobile() {
return this.$store.state.globals.isMobile
},
isMobileLandscape() {
return this.$store.state.globals.isMobileLandscape
},
isMobilePortrait() {
return this.$store.state.globals.isMobilePortrait
},
drawerOpen() {
return !this.isMobilePortrait || this.isOpen
},
routeName() {
return this.$route.name
},
versionData() {
return this.$store.state.versionData || {}
},
hasUpdate() {
return !!this.versionData.hasUpdate
},
latestVersion() {
return this.versionData.latestVersion
},
githubTagUrl() {
return this.versionData.githubTagUrl
},
currentVersionChangelog() {
return this.versionData.currentVersionChangelog || 'No Changelog Available'
},
streamLibraryItem() {
return this.$store.state.streamLibraryItem
}
},
methods: {
clickChangelog() {
this.showChangelogModal = true
},
clickOutside() {
if (!this.isOpen) return
this.closeDrawer()
},
closeDrawer() {
this.$emit('update:isOpen', false)
}
}
}
</script>