<template> <header class="header"> <nav class="nav"> <Link href="/"> <h1 class="title"> <div class="logo" v-html="logo" /> </h1> </Link> <ul class="nav-list nolist"> <!-- <li class="nav-item"> <Link class="link" :class="{ active: activePage === 'updates' }" href="/updates" >Updates</Link> </li> --> <li class="nav-item"> <Link class="link" :class="{ active: activePage === 'actors' }" href="/actors" >Actors</Link> </li> <li class="nav-item"> <Link class="link" :class="{ active: activePage === 'channels' }" href="/channels" >Channels</Link> </li> <li class="nav-item"> <Link class="link" :class="{ active: activePage === 'tags' }" href="/tags" >Tags</Link> </li> <li class="nav-item"> <Link class="link" :class="{ active: activePage === 'movies' }" href="/movies" >Movies</Link> </li> </ul> </nav> <div class="header-section"> <form class="search" :class="{ focused: searchFocused }" @submit.prevent="search" > <input v-model="query" type="search" placeholder="Search" class="input" @focus="searchFocused = true" @blur="searchFocused = false" > <button class="search-button"> <Icon icon="search" /> </button> </form> <VDropdown v-if="user" :triggers="['click']" :prevent-overflow="true" > <div class="userpanel" :class="{ searching: searchFocused }" > <img :src="user.avatar" class="avatar" > </div> <template #popper> <div class="menu"> <a :href="`/user/${user.username}`" class="menu-header ellipsis" >{{ user.username }}</a> <ul class="menu-list nolist"> <li class="menu-item"> <a :href="`/user/${user.username}`" class="menu-button nolink" > <Icon icon="vcard" /> View profile </a> </li> <li v-if="user.primaryStash" class="menu-item" > <a :href="`/stash/${user.username}/${user.primaryStash.slug}`" class="menu-button nolink favorites" > <Icon icon="heart7" /> Favorites </a> </li> <li class="menu-button menu-item logout" @click="logout" ><Icon icon="exit2" />Log out</li> </ul> </div> </template> </VDropdown> <div v-else-if="allowLogin" class="userpanel" :class="{ searching: searchFocused }" > <a :href="`/login?r=${encodeURIComponent(currentPath)}`" class="login button button-submit nolink" > <span class="login-text">Log in</span> <Icon icon="enter" /> </a> </div> </div> </header> </template> <script setup> import { ref, computed, inject } from 'vue'; import navigate from '#/src/navigate.js'; import { del } from '#/src/api.js'; import logo from '../../assets/img/logo.svg?raw'; // eslint-disable-line import/no-unresolved const pageContext = inject('pageContext'); const user = pageContext.user; const query = ref(pageContext.urlParsed.search.q || ''); const allowLogin = pageContext.env.allowLogin; const searchFocused = ref(false); const activePage = computed(() => pageContext.urlParsed.pathname.split('/')[1]); const currentPath = `${pageContext.urlParsed.pathnameOriginal}${pageContext.urlParsed.searchOriginal || ''}`; function search() { navigate('/search', { q: query.value }, { redirect: true }); } async function logout() { del('/session'); navigate('/login', null, { redirect: true }); } </script> <style scoped> .header { display: flex; align-items: center; justify-content: space-between; position: relative; z-index: 100; /* make sure shadow shows up above content */ box-shadow: 0 0 3px var(--shadow-weak-10); } .title { margin: 0; display: inline-block; } .logo { display: flex; width: 8rem; box-sizing: border-box; padding: .75rem; margin-right: 1rem; fill: var(--primary); } .nav { display: flex; align-items: center; } .nav-item .link { font-size: .9rem; color: var(--shadow-strong-10); box-sizing: border-box; padding: 1rem; height: 100%; &:hover { text-decoration: none; color: var(--primary); } } .link { font-weight: bold; &.active { color: var(--primary); } } .header-section { height: 100%; display: flex; align-items: center; margin-left: .5rem; } .search { height: 2rem; display: flex; align-items: center; border-radius: 1rem; background: var(--background-dark-10); box-shadow: inset 0 0 3px var(--shadow-weak-40); .input { width: 14rem; padding: .5rem 0 .5rem 1rem; border: none; margin: 0; background: none; &:placeholder-shown { text-overflow: ellipsis; } } .search-button { padding: 0; border: none; background: none; } .icon { height: 100%; padding: 0 .75rem 0 .5rem; fill: var(--shadow-weak-10); } &.focused { /* border: solid 1px var(--primary-light-10); */ box-shadow: inset 0 0 3px var(--shadow-weak-30); .icon { fill: var(--primary); cursor: pointer; } } &:last-child { /* login disabled */ margin-right: 1rem; } } .userpanel { height: 100%; display: flex; align-items: center; padding: 0 1rem 0 1.5rem; font-size: 0; cursor: pointer; &:hover .avatar { box-shadow: 0 0 3px var(--shadow-weak-10); } } .avatar { width: 2rem; height: 2rem; border-radius: .25rem; object-fit: cover; } .login { display: flex; align-items: center; .icon { display: none; padding: 0; fill: var(--text-light); } } .menu { overflow: hidden; } .menu-header { display: flex; padding: .75rem 1rem; border-bottom: solid 1px var(--shadow-weak-30); color: var(--shadow-strong-30); text-decoration: none; font-weight: bold; } .menu-item { display: block; } .menu-button { display: flex; align-items: center; padding: .5rem .5rem .5rem .75rem; font-size: 1.1rem; .icon { fill: var(--shadow); margin-right: .75rem; transform: translateY(-1px); } &:hover { background: var(--shadow-weak-30); cursor: pointer; } } /* .favorites .icon { fill: var(--primary); } */ .logout .icon { fill: var(--error); } @media(--small) { .header-section { flex-grow: 1; } .search { flex-grow: 1; } .search .input { width: 0; flex-grow: 1; } .userpanel.searching { padding: 0 0 0 1rem; width: 0; } .login-text { display: none; } .login .icon { display: block; } } @media(--small-10) { .nav-list { display: none; } .logo { margin-right: .75rem; } .userpanel { padding-left: 1rem; } } </style>