added a sidebar drawer to improve mobile responsiveness
This commit is contained in:
11
src/App.vue
11
src/App.vue
@@ -5,6 +5,7 @@ import DocumentLocaleSetter from '@/components/locale/DocumentLocaleSetter.vue';
|
|||||||
import NavBar from '@/components/NavBar.vue';
|
import NavBar from '@/components/NavBar.vue';
|
||||||
import LocaleSaver from '@/components/locale/LocaleSaver.vue';
|
import LocaleSaver from '@/components/locale/LocaleSaver.vue';
|
||||||
import StoragePersist from '@/components/StoragePersist.vue';
|
import StoragePersist from '@/components/StoragePersist.vue';
|
||||||
|
import SideBar from '@/components/SideBar.vue';
|
||||||
|
|
||||||
@Options({
|
@Options({
|
||||||
name: 'App',
|
name: 'App',
|
||||||
@@ -13,20 +14,26 @@ import StoragePersist from '@/components/StoragePersist.vue';
|
|||||||
LocaleSaver,
|
LocaleSaver,
|
||||||
NavBar,
|
NavBar,
|
||||||
RouterView,
|
RouterView,
|
||||||
|
SideBar,
|
||||||
StoragePersist,
|
StoragePersist,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
export default class App extends Vue {
|
export default class App extends Vue {
|
||||||
|
sidebarToggled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="d-flex flex-column h-100 w-100">
|
<div class="h-100 w-100">
|
||||||
<LocaleSaver/>
|
<LocaleSaver/>
|
||||||
<DocumentLocaleSetter/>
|
<DocumentLocaleSetter/>
|
||||||
<StoragePersist/>
|
<StoragePersist/>
|
||||||
<NavBar/>
|
<SideBar ref="sidebar" class="h-100 w-100" :toggled="sidebarToggled"
|
||||||
|
@close="sidebarToggled=false"/>
|
||||||
|
<div class="d-flex flex-column h-100 w-100">
|
||||||
|
<NavBar @toggleSidebar="sidebarToggled = !sidebarToggled"/>
|
||||||
<RouterView class="flex-grow-1"/>
|
<RouterView class="flex-grow-1"/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ import LocaleSelector from '@/components/locale/LocaleSelector.vue';
|
|||||||
LocaleSelector,
|
LocaleSelector,
|
||||||
MangaUpdatesUpdater,
|
MangaUpdatesUpdater,
|
||||||
},
|
},
|
||||||
|
emits: {
|
||||||
|
'toggleSidebar': undefined,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
export default class NavBar extends Vue {
|
export default class NavBar extends Vue {
|
||||||
}
|
}
|
||||||
@@ -20,51 +23,52 @@ export default class NavBar extends Vue {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<nav class="navbar navbar-expand border-bottom shadow">
|
<!-- z-index needed, otherwise shadow not showing -->
|
||||||
<div class="container-fluid">
|
<nav class="navbar border-bottom shadow flex-nowrap" style="z-index: 1">
|
||||||
<!-- <a class="navbar-brand" href="#">Navbar</a>-->
|
<div>
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"
|
<div class="navbar-sidebar-toggler mx-1 c-pointer">
|
||||||
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
<i class="fa fa-bars me-2" @click="$emit('toggleSidebar')"/>
|
||||||
<span class="navbar-toggler-icon"></span>
|
</div>
|
||||||
</button>
|
</div>
|
||||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
<div>
|
||||||
<!-- <ul class="navbar-nav me-auto mb-2 mb-lg-0">-->
|
|
||||||
<!-- <li class="nav-item">-->
|
|
||||||
<!-- <a class="nav-link active" aria-current="page" href="#">Home</a>-->
|
|
||||||
<!-- </li>-->
|
|
||||||
<!-- <li class="nav-item">-->
|
|
||||||
<!-- <a class="nav-link" href="#">Link</a>-->
|
|
||||||
<!-- </li>-->
|
|
||||||
<!-- <li class="nav-item dropdown">-->
|
|
||||||
<!-- <a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">-->
|
|
||||||
<!-- Dropdown-->
|
|
||||||
<!-- </a>-->
|
|
||||||
<!-- <ul class="dropdown-menu">-->
|
|
||||||
<!-- <li><a class="dropdown-item" href="#">Action</a></li>-->
|
|
||||||
<!-- <li><a class="dropdown-item" href="#">Another action</a></li>-->
|
|
||||||
<!-- <li>-->
|
|
||||||
<!-- <hr class="dropdown-divider">-->
|
|
||||||
<!-- </li>-->
|
|
||||||
<!-- <li><a class="dropdown-item" href="#">Something else here</a></li>-->
|
|
||||||
<!-- </ul>-->
|
|
||||||
<!-- </li>-->
|
|
||||||
<!-- <li class="nav-item">-->
|
|
||||||
<!-- <a class="nav-link disabled">Disabled</a>-->
|
|
||||||
<!-- </li>-->
|
|
||||||
<!-- </ul>-->
|
|
||||||
<div class="mx-auto">
|
|
||||||
<div class="d-flex flex-row">
|
<div class="d-flex flex-row">
|
||||||
<AniListUserSearch class="mx-1"/>
|
<AniListUserSearch class="me-2"/>
|
||||||
<MangaUpdatesUpdater class="mx-1"/>
|
<MangaUpdatesUpdater class=""/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="d-flex flex-row align-items-center">
|
<div class="d-flex flex-row align-items-center">
|
||||||
<LocaleSelector class="ms-2"/>
|
<LocaleSelector class="navbar-locale-select"/>
|
||||||
<BootstrapThemeSwitch class="ms-2"/>
|
<BootstrapThemeSwitch class="navbar-theme-switch ms-2"/>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import 'bootstrap/scss/functions';
|
||||||
|
@import 'bootstrap/scss/variables';
|
||||||
|
@import 'bootstrap/scss/mixins/breakpoints';
|
||||||
|
|
||||||
|
@include media-breakpoint-down(sm) {
|
||||||
|
nav.navbar {
|
||||||
|
--bs-navbar-padding-x: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.navbar .navbar-locale-select,
|
||||||
|
nav.navbar .navbar-theme-switch {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include media-breakpoint-up(sm) {
|
||||||
|
nav.navbar {
|
||||||
|
--bs-navbar-padding-x: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.navbar .navbar-sidebar-toggler {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|||||||
87
src/components/SideBar.vue
Normal file
87
src/components/SideBar.vue
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {Options, Vue} from 'vue-class-component';
|
||||||
|
import SideBarNavItem from '@/components/SideBarNavItem.vue';
|
||||||
|
import {Prop} from 'vue-property-decorator';
|
||||||
|
import LocaleSelector from '@/components/locale/LocaleSelector.vue';
|
||||||
|
import BootstrapThemeSwitch from '@/components/bootstrapThemeSwitch/BootstrapThemeSwitch.vue';
|
||||||
|
import SideBarHead from '@/components/SideBarHead.vue';
|
||||||
|
|
||||||
|
@Options({
|
||||||
|
name: 'SideBar',
|
||||||
|
components: {
|
||||||
|
BootstrapThemeSwitch,
|
||||||
|
LocaleSelector,
|
||||||
|
SideBarHead,
|
||||||
|
SideBarNavItem,
|
||||||
|
},
|
||||||
|
emits: {
|
||||||
|
'close': undefined,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
export default class SideBar extends Vue {
|
||||||
|
@Prop({default: false})
|
||||||
|
toggled!: boolean;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="{ toggled: toggled }" class="sidebar border-right shadow bg-body">
|
||||||
|
<div class="d-flex flex-column">
|
||||||
|
<ul class="nav flex-column mb-auto">
|
||||||
|
<li>
|
||||||
|
<SideBarHead @close="$emit('close')"/>
|
||||||
|
</li>
|
||||||
|
<SideBarNavItem :text="$t('locale')" faIcon="fa-language">
|
||||||
|
<template #end>
|
||||||
|
<LocaleSelector class="navbar-locale-select me-1"/>
|
||||||
|
</template>
|
||||||
|
</SideBarNavItem>
|
||||||
|
<SideBarNavItem :text="$t('design')" faIcon="fa-moon">
|
||||||
|
<template #end>
|
||||||
|
<BootstrapThemeSwitch class="navbar-theme-switch"/>
|
||||||
|
</template>
|
||||||
|
</SideBarNavItem>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import 'bootstrap/scss/functions';
|
||||||
|
@import 'bootstrap/scss/variables';
|
||||||
|
@import 'bootstrap/scss/mixins/breakpoints';
|
||||||
|
|
||||||
|
$width: 15em;
|
||||||
|
|
||||||
|
@include media-breakpoint-down(sm) {
|
||||||
|
.sidebar {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: $width;
|
||||||
|
max-width: #{'clamp(0em, '}$width#{', 100%)'};
|
||||||
|
transition: width ease-in-out 0.25s;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
z-index: 10000;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
&:not(.toggled) {
|
||||||
|
width: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-nav-item {
|
||||||
|
border-top: 0;
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include media-breakpoint-up(sm) {
|
||||||
|
.sidebar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
24
src/components/SideBarHead.vue
Normal file
24
src/components/SideBarHead.vue
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {Options, Vue} from 'vue-class-component';
|
||||||
|
|
||||||
|
@Options({
|
||||||
|
name: 'SideBar',
|
||||||
|
components: {},
|
||||||
|
emits: {
|
||||||
|
'close': undefined,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
export default class SideBar extends Vue {
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<nav class="navbar border-bottom shadow flex-nowrap">
|
||||||
|
<div class="sidebar-closer mx-1 c-pointer">
|
||||||
|
<i class="fa fa-arrow-left me-2" @click="$emit('close')"/>
|
||||||
|
|
||||||
|
<!-- 'hack' to ensure this nav is the same height as the navbar -->
|
||||||
|
<input class="form-control d-inline mx-0 px-0" style="visibility: hidden; width: 0;"/>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</template>
|
||||||
41
src/components/SideBarNavItem.vue
Normal file
41
src/components/SideBarNavItem.vue
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {Options, Vue} from 'vue-class-component';
|
||||||
|
import {Prop} from 'vue-property-decorator';
|
||||||
|
|
||||||
|
@Options({
|
||||||
|
name: 'SideBarNavItem',
|
||||||
|
components: {},
|
||||||
|
})
|
||||||
|
export default class SideBarNavItem extends Vue {
|
||||||
|
@Prop({required: false})
|
||||||
|
faIcon!: string;
|
||||||
|
@Prop({required: false})
|
||||||
|
text!: string;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<li class="nav-item sidebar-nav-item">
|
||||||
|
<slot>
|
||||||
|
<div class="d-flex flex-row p-2 align-items-center">
|
||||||
|
<i :class="[faIcon]" class="sidebar-nav-item-icon text-center fa"></i>
|
||||||
|
<span class="sidebar-nav-item-text me-auto">{{ text }}</span>
|
||||||
|
<slot name="end"/>
|
||||||
|
</div>
|
||||||
|
</slot>
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.sidebar-nav-item-text {
|
||||||
|
opacity: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
max-width: 10em;
|
||||||
|
transition: display ease-in-out 0.25s, opacity ease-in-out 0.25s, max-width ease-in-out 0.25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-nav-item-icon {
|
||||||
|
width: 2em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -13,8 +13,8 @@ export default class LocaleSelector extends Vue {
|
|||||||
|
|
||||||
get supportedLocales(): { value: string, text: string } [] {
|
get supportedLocales(): { value: string, text: string } [] {
|
||||||
return [
|
return [
|
||||||
{value: 'en', text: this.$t('locale.en')},
|
{value: 'en', text: this.$t('locales.en')},
|
||||||
{value: 'de', text: this.$t('locale.de')},
|
{value: 'de', text: this.$t('locales.de')},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import {messagesEn} from '@/locale/en';
|
import {messagesEn} from '@/locale/en';
|
||||||
|
|
||||||
export const messagesDe = {
|
export const messagesDe = {
|
||||||
|
design: 'Design',
|
||||||
fetch: {
|
fetch: {
|
||||||
mangaUpdates: {
|
mangaUpdates: {
|
||||||
chapters: 'Kapitel',
|
chapters: 'Kapitel',
|
||||||
@@ -10,7 +11,8 @@ export const messagesDe = {
|
|||||||
starting: 'Starte',
|
starting: 'Starte',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
locale: messagesEn.locale,
|
locale: 'Sprache',
|
||||||
|
locales: messagesEn.locales,
|
||||||
localStoragePersistNotSupported: 'Lokaler Speicher wird nicht permanent. Das könnte in längere Wartezeiten verursachen!',
|
localStoragePersistNotSupported: 'Lokaler Speicher wird nicht permanent. Das könnte in längere Wartezeiten verursachen!',
|
||||||
manga: {
|
manga: {
|
||||||
chapters: {
|
chapters: {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export const messagesEn = {
|
export const messagesEn = {
|
||||||
|
design: 'Design',
|
||||||
fetch: {
|
fetch: {
|
||||||
mangaUpdates: {
|
mangaUpdates: {
|
||||||
chapters: 'Chapters',
|
chapters: 'Chapters',
|
||||||
@@ -8,7 +9,8 @@ export const messagesEn = {
|
|||||||
starting: 'Starting',
|
starting: 'Starting',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
locale: {
|
locale: 'Language',
|
||||||
|
locales: {
|
||||||
'de': 'Deutsch',
|
'de': 'Deutsch',
|
||||||
'en': 'English',
|
'en': 'English',
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user