Added mobile navigation.

This commit is contained in:
DebaucheryLibrarian 2024-03-18 01:47:49 +01:00
parent fe0fe38f5a
commit 77d3060b98
6 changed files with 326 additions and 7 deletions
assets/css
components
renderer

View File

@ -1,3 +1,5 @@
@custom-media --small-50 (max-width: 350px);
@custom-media --small-40 (max-width: 480px);
@custom-media --small-30 (max-width: 540px); @custom-media --small-30 (max-width: 540px);
@custom-media --small-20 (max-width: 650px); @custom-media --small-20 (max-width: 650px);
@custom-media --small-10 (max-width: 768px); @custom-media --small-10 (max-width: 768px);

View File

@ -0,0 +1,110 @@
<template>
<nav class="nav-list noselect">
<a
href="/updates"
class="nav-item updates nolink"
:class="{ active: activePage === 'updates' }"
>
<div class="nav-pill">Updates</div>
</a>
<a
href="/actors"
class="nav-item actors nolink"
:class="{ active: activePage === 'actors' }"
>
<div class="nav-pill">Actors</div>
</a>
<a
href="/channels"
class="nav-item channels nolink"
:class="{ active: activePage === 'channels' }"
>
<div class="nav-pill">Channels</div>
</a>
<button
class="nav-item nav-button"
@click="emit('sidebar')"
>
<div class="nav-pill">
<Icon
icon="menu"
/>
</div>
</button>
</nav>
</template>
<script setup>
import { computed, inject } from 'vue';
const emit = defineEmits(['sidebar']);
const pageContext = inject('pageContext');
const activePage = computed(() => pageContext.urlParsed.pathname.split('/')[1]);
</script>
<style scoped>
.nav-list {
display: flex;
align-items: stretch;
flex-shrink: 0;
position: relative; /* required for box-shadow to show */
background: var(--grey-dark-40);
box-shadow: 0 0 3px var(--shadow-weak-10);
}
.nav-item {
display: flex;
justify-content: center;
align-items: center;
flex-grow: 1;
padding: .5rem;
border: none;
background: none;
color: var(--highlight-strong-20);
font-weight: bold;
font-size: .9rem;
&:hover {
cursor: pointer;
.nav-pill {
color: var(--text-light);
background: var(--highlight-weak-30);
}
.icon {
fill: var(--text-light);
}
}
&.active {
color: var(--text-light);
}
.icon {
width: 1.5rem;
height: 1.5rem;
fill: var(--highlight-strong-10);
}
}
.nav-pill {
width: 100%;
height: 1rem;
display: flex;
justify-content: center;
align-items: center;
padding: .5rem 1rem;
border-radius: 1.5rem;
}
@media(--small-50) {
.nav-item.channels {
display: none;
}
}
</style>

View File

@ -159,7 +159,8 @@ async function logout() {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
z-index: 1000; /* make sure shadow shows up above content */ position: relative;
z-index: 100; /* make sure shadow shows up above content */
box-shadow: 0 0 3px var(--shadow-weak-10); box-shadow: 0 0 3px var(--shadow-weak-10);
} }
@ -192,6 +193,7 @@ async function logout() {
&:hover { &:hover {
text-decoration: none; text-decoration: none;
color: var(--primary);
} }
} }
@ -223,6 +225,10 @@ async function logout() {
border: none; border: none;
margin: 0; margin: 0;
background: none; background: none;
&:placeholder-shown {
text-overflow: ellipsis;
}
} }
.search-button { .search-button {
@ -321,7 +327,7 @@ async function logout() {
@media(--small) { @media(--small) {
.search .input { .search .input {
width: 10rem; width: 12rem;
} }
} }
@ -329,5 +335,18 @@ async function logout() {
.nav-list { .nav-list {
display: none; display: none;
} }
.header-section {
flex-grow: 1;
}
.search {
flex-grow: 1;
}
.search .input {
width: 0;
flex-grow: 1;
}
} }
</style> </style>

View File

@ -299,19 +299,23 @@ function updateFilter(prop, value, reload = true) {
} }
.scopes { .scopes {
margin-left: 1rem; display: flex;
gap: .5rem;
margin: .75rem 0 .25rem 1rem;
} }
.scope { .scope {
box-sizing: border-box; box-sizing: border-box;
padding: 1rem; padding: .5rem 1rem;
background: var(--background-dark-20);
border-radius: 1rem;
color: var(--shadow); color: var(--shadow);
font-size: .9rem; font-size: .9rem;
font-weight: bold; font-weight: bold;
&.active { &.active {
color: var(--primary); background: var(--primary);
font-weight: bold; color: var(--text-light);
} }
} }

View File

@ -0,0 +1,137 @@
<template>
<div
class="sidebar-container"
@click="emit('sidebar')"
>
<div
class="sidebar"
@click.stop
>
<Link href="/updates">
<h1 class="title">
<div
class="logo"
v-html="logo"
/>
</h1>
</Link>
<ul class="nolist menu">
<li
class="menu-item"
:class="{ active: activePage === 'updates' }"
>
<a
href="/updates"
class="menu-link nolink"
>Updates</a>
</li>
<li
class="menu-item"
:class="{ active: activePage === 'actors' }"
>
<a
href="/actors"
class="menu-link nolink"
>Actors</a>
</li>
<li
class="menu-item"
:class="{ active: activePage === 'channels' }"
>
<a
href="/channels"
class="menu-link nolink"
>Channels</a>
</li>
<li
class="menu-item"
:class="{ active: activePage === 'tags' }"
>
<a
href="/tags"
class="menu-link nolink"
>Tags</a>
</li>
<li
class="menu-item"
:class="{ active: activePage === 'movies' }"
>
<a
href="/movies"
class="menu-link nolink"
>Movies</a>
</li>
</ul>
</div>
</div>
</template>
<script setup>
import { computed, inject } from 'vue';
import logo from '../../assets/img/logo.svg?raw'; // eslint-disable-line import/no-unresolved
const emit = defineEmits(['sidebar']);
const pageContext = inject('pageContext');
const activePage = computed(() => pageContext.urlParsed.pathname.split('/')[1]);
</script>
<style scoped>
.sidebar-container {
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
z-index: 200;
display: flex;
justify-content: flex-end;
background: var(--shadow-strong-10);
}
.sidebar {
width: 15rem;
max-width: 100%;
height: 100%;
margin-left: 2rem;
background: var(--background);
box-shadow: 0 0 3px var(--shadow-strong-30);
}
.title {
margin: 0;
}
.logo {
width: 6rem;
max-width: 100%;
padding: .5rem 1rem .75rem 1rem;
fill: var(--primary);
}
.menu-item {
display: block;
&.active .menu-link {
color: var(--primary);
}
}
.menu-link {
display: block;
padding: .75rem 1rem;
font-size: 1.1rem;
font-weight: bold;
color: var(--shadow-strong-10);
&:hover {
color: var(--primary);
}
}
</style>

View File

@ -3,6 +3,13 @@
class="container" class="container"
@click="blur" @click="blur"
> >
<transition name="slide">
<Sidebar
v-if="showSidebar"
@sidebar="showSidebar = false"
/>
</transition>
<Header /> <Header />
<div <div
@ -11,6 +18,11 @@
> >
<slot @scroll="scroll" /> <slot @scroll="scroll" />
</div> </div>
<BottomNavigation
class="nav"
@sidebar="showSidebar = true"
/>
</div> </div>
</template> </template>
@ -19,9 +31,12 @@ import { ref, onMounted } from 'vue';
import events from '#/src/events.js'; import events from '#/src/events.js';
import Header from '../components/header/header.vue'; import Header from '#/components/header/header.vue';
import Sidebar from '#/components/sidebar/sidebar.vue';
import BottomNavigation from '#/components/footer/navigation.vue';
const content = ref(null); const content = ref(null);
const showSidebar = ref(false);
function blur() { function blur() {
events.emit('blur'); events.emit('blur');
@ -44,6 +59,28 @@ onMounted(() => {
display: flex; display: flex;
align-items: center; align-items: center;
} }
.slide-enter-active,
.slide-leave-active {
&.sidebar-container {
transition: background .15s ease-in-out;
}
.sidebar {
transition: transform .15s ease-in-out;
}
}
.slide-enter-from,
.slide-leave-to {
&.sidebar-container {
background: transparent;
}
.sidebar {
transform: translate(100%, 0);
}
}
</style> </style>
<style scoped> <style scoped>
@ -60,4 +97,14 @@ onMounted(() => {
flex-grow: 1; flex-grow: 1;
overflow-y: auto; overflow-y: auto;
} }
.nav {
display: none;
}
@media(--small-10) {
.nav {
display: flex;
}
}
</style> </style>