Split navigation icons/header into more components, fixed svg transition

Split more out into `Hamburger` & `NavigationIcon` components.
This commit is contained in:
2022-01-13 00:17:43 +01:00
parent b021882013
commit d3a3160cf8
12 changed files with 318 additions and 317 deletions

View File

@@ -6,75 +6,15 @@
<SearchInput />
<div class="nav__hamburger" @click="toggleNav">
<div v-for="_ in 3" class="bar"></div>
</div>
<Hamburger @click="toggleNav" />
<router-link class="profile desktop-only" to="/profile">
<li class="navigation-link">
<icon-profile class="navigation-icon" />
<span>{{ !userLoggedIn ? "Signin" : "Profile" }}</span>
</li>
</router-link>
<!-- <NavigationIcons class="desktop-only" /> -->
<NavigationIcon class="desktop-only" :route="profileRoute" />
<div class="nav__list mobile-only">
<div class="nav__list mobile-only" :class="{ open: hamburgerMenuOpen }">
<NavigationIcons>
<router-link
v-if="userLoggedIn"
class="settings"
to="/profile?settings=true"
>
<li class="navigation-link">
<icon-settings class="navigation-icon stroke" />
<span>Settings</span>
</li>
</router-link>
<router-link v-if="userLoggedIn" class="profile" to="/activity">
<li class="navigation-link">
<icon-activity class="navigation-icon stroke" />
<span>Activity</span>
</li>
</router-link>
<router-link class="profile" to="/profile">
<li class="navigation-link">
<icon-profile class="navigation-icon" />
<span>{{ !userLoggedIn ? "Signin" : "Profile" }}</span>
</li>
</router-link>
<NavigationIcon :route="profileRoute" />
</NavigationIcons>
<!-- <li class="nav__item nav__item--profile">
<router-link
class="nav__link nav__link--profile"
:to="{ name: 'signin' }"
v-if="!userLoggedIn"
>
<div class="nav__link-wrap">
<svg class="nav__link-icon">
<use xlink:href="#iconLogin"></use>
</svg>
<span class="nav__link-title">Sign in</span>
</div>
</router-link>
<router-link
class="nav__link nav__link--profile"
:to="{ name: 'profile' }"
v-if="userLoggedIn"
>
<div class="nav__link-wrap">
<svg class="nav__link-icon">
<use xlink:href="#iconLogin"></use>
</svg>
<span class="nav__link-title">Profile</span>
</div>
</router-link>
</li> -->
</div>
<div style="z-index: -1"></div>
</nav>
</template>
@@ -86,110 +26,45 @@ import IconSettings from "../icons/IconSettings";
import IconActivity from "../icons/IconActivity";
import SearchInput from "@/components/SearchInput";
import NavigationIcons from "src/components/NavigationIcons";
import NavigationIcon from "src/components/ui/NavigationIcon";
import Hamburger from "@/components/ui/Hamburger";
import { mapGetters, mapActions } from "vuex";
export default {
components: {
NavigationIcons,
NavigationIcon,
SearchInput,
TmdbLogo,
IconProfile,
IconSettings,
IconActivity
IconActivity,
Hamburger
},
data() {
return {
listTypes: storage.homepageLists,
userLoggedIn: localStorage.getItem("token") ? true : false
hamburgerMenuOpen: false
};
},
methods: {
setUserStatus() {
this.userLoggedIn = localStorage.getItem("token") ? true : false;
},
toggleNav() {
document
.querySelector(".nav__hamburger")
.classList.toggle("nav__hamburger--active");
document
.querySelector(".nav__list")
.classList.toggle("nav__list--active");
this.hamburgerMenuOpen = !this.hamburgerMenuOpen;
}
},
created() {
// TODO move this to state manager
eventHub.$on("setUserStatus", this.setUserStatus);
computed: {
...mapGetters("user", ["loggedIn"]),
profileRoute() {
return {
title: !this.loggedIn ? "Signin" : "Profile",
route: !this.loggedIn ? "/signin" : "/profile",
icon: IconProfile
};
}
}
};
</script>
<style lang="scss">
@import "./src/scss/media-queries";
.profile.desktop-only .navigation-link,
.navigation-link {
border-bottom: none;
border-left: 1px solid var(--text-color-5);
}
.navigation-link {
display: grid;
place-items: center;
list-style: none;
padding: 1rem 0.15rem;
margin: 0;
text-align: center;
background-color: var(--background-color-secondary);
transition: transform 0.3s ease, color 0.3s ease, stoke 0.3s ease,
fill 0.3s ease, background-color 0.5s ease;
@include mobile {
padding: 1rem;
width: 50vw;
}
&:hover {
transform: scale(1.05);
}
&:hover,
&.active {
background-color: var(--background-color);
span,
.navigation-icon {
color: var(--text-color);
fill: var(--text-color);
&.stroke {
fill: none;
stroke: var(--text-color);
}
}
}
span {
text-transform: uppercase;
margin-top: 0.75rem;
font-size: 11px;
color: var(--text-color-70);
}
}
a {
text-decoration: none;
}
.navigation-icon {
width: 28px;
fill: var(--text-color-70);
transition: inherit;
&.stroke {
fill: none;
stroke: var(--text-color-70);
}
}
</style>
<style lang="scss" scoped>
@import "./src/scss/variables";
@import "./src/scss/media-queries";
@@ -213,6 +88,12 @@ nav {
.logo {
padding: 1rem;
fill: var(--color-green);
width: var(--header-size);
height: var(--header-size);
display: flex;
align-items: center;
justify-content: center;
background: $background-nav-logo;
transition: transform 0.3s ease;
&:hover {
@@ -224,123 +105,21 @@ nav {
}
}
.nav {
// transition: background 0.5s ease;
// position: fixed;
// top: 0;
// left: 0;
// width: 100%;
// height: 50px;
// z-index: 10;
// display: block;
// color: $text-color;
// background-color: $background-color-secondary;
.nav__list {
display: flex;
flex-wrap: wrap;
position: fixed;
top: var(--header-size);
left: 0;
width: 100%;
background-color: $background-95;
visibility: hidden;
opacity: 0;
transition: opacity 0.4s ease;
&__logo {
width: var(--header-size);
height: var(--header-size);
display: flex;
align-items: center;
justify-content: center;
background: $background-nav-logo;
// &-image {
// width: 35px;
// height: 31px;
// fill: $green;
// transition: transform 0.5s ease;
// @include tablet-min {
// width: 45px;
// height: 40px;
// }
// }
// &:hover &-image {
// transform: scale(1.04);
// }
}
&__hamburger {
display: block;
position: relative;
width: var(--header-size);
height: var(--header-size);
cursor: pointer;
border-left: 1px solid $background-color;
background-color: var(--background-color-secondary);
@include tablet-min {
display: none;
}
.bar {
position: absolute;
width: 23px;
height: 1px;
background-color: $text-color-70;
transition: all 300ms ease;
&:nth-child(1) {
left: 16px;
top: 17px;
}
&:nth-child(2) {
left: 16px;
top: 25px;
&:after {
content: "";
position: absolute;
left: 0px;
top: 0px;
width: 23px;
height: 1px;
transition: all 300ms ease;
}
}
&:nth-child(3) {
right: 15px;
top: 33px;
}
}
&--active {
.bar {
&:nth-child(1),
&:nth-child(3) {
width: 0;
}
&:nth-child(2) {
transform: rotate(-45deg);
}
&:nth-child(2):after {
transform: rotate(-90deg);
// background: rgba($c-dark, 0.5);
background-color: $text-color-70;
}
}
}
}
&__list {
width: 100%;
position: fixed;
left: 0;
top: var(--header-size);
opacity: 0;
transition: opacity 0.4s ease;
visibility: hidden;
@include mobile-only {
display: flex;
flex-wrap: wrap;
background-color: $background-95;
&--active {
opacity: 1;
visibility: visible;
}
}
@include tablet-min {
display: flex;
position: relative;
display: block;
width: 100%;
border-top: 0;
top: 0;
}
&.open {
opacity: 1;
visibility: visible;
}
}
</style>

View File

@@ -1,32 +1,34 @@
<template>
<ul class="navigation-icons">
<router-link
:to="{ path: route.route }"
<NavigationIcon
v-for="route in routes"
:key="route.title"
>
<li
class="navigation-link"
:class="{ active: route.route == activeRoute }"
>
<component class="navigation-icon" :is="route.icon"></component>
<span>{{ route.title }}</span>
</li>
</router-link>
:route="route"
:active="activeRoute"
/>
<slot></slot>
</ul>
</template>
<script>
import NavigationIcon from "./ui/NavigationIcon";
import IconRequest from "../icons/IconRequest";
import IconNowPlaying from "../icons/IconNowPlaying";
import IconPopular from "../icons/IconPopular";
import IconUpcoming from "../icons/IconUpcoming";
import IconSettings from "../icons/IconSettings";
import IconActivity from "../icons/IconActivity";
export default {
name: "NavigationIcons",
components: { IconRequest, IconPopular, IconNowPlaying, IconUpcoming },
components: {
NavigationIcon,
IconRequest,
IconPopular,
IconNowPlaying,
IconUpcoming,
IconSettings,
IconActivity
},
data() {
return {
routes: [],
@@ -68,6 +70,18 @@ export default {
route: "/list/upcoming",
apiPath: "/v2/movie/upcoming",
icon: IconUpcoming
},
{
title: "Activity",
route: "/activity",
requiresAuth: true,
icon: IconActivity
},
{
title: "Settings",
route: "/profile?settings=true",
requiresAuth: true,
icon: IconSettings
}
];
}

View File

@@ -0,0 +1,90 @@
<template>
<div
class="nav__hamburger"
:class="{ open }"
@click="toggleNav"
@keydown.enter="toggleNav"
tabindex="0"
>
<div v-for="_ in 3" class="bar"></div>
</div>
</template>
<script>
export default {
data() {
return {
open: false
};
},
methods: {
toggleNav() {
this.open = !this.open;
this.$emit("click", this.open);
}
}
};
</script>
<style lang="scss" scoped>
@import "./src/scss/media-queries";
.nav__hamburger {
display: block;
position: relative;
width: var(--header-size);
height: var(--header-size);
cursor: pointer;
border-left: 1px solid var(--background-color);
background-color: var(--background-color-secondary);
@include tablet-min {
display: none;
}
.bar {
position: absolute;
width: 23px;
height: 1px;
background-color: var(--text-color-70);
transition: all 300ms ease;
&:nth-child(1) {
left: 16px;
top: 17px;
}
&:nth-child(2) {
left: 16px;
top: 25px;
&:after {
content: "";
position: absolute;
left: 0px;
top: 0px;
width: 23px;
height: 1px;
transition: all 300ms ease;
}
}
&:nth-child(3) {
right: 15px;
top: 33px;
}
}
&.open {
.bar {
&:nth-child(1),
&:nth-child(3) {
width: 0;
}
&:nth-child(2) {
transform: rotate(-45deg);
}
&:nth-child(2):after {
transform: rotate(-90deg);
// background: rgba($c-dark, 0.5);
background-color: var(--text-color-70);
}
}
}
}
</style>

View File

@@ -0,0 +1,90 @@
<template>
<router-link
:to="{ path: route.route }"
:key="route.title"
v-if="
route.requiresAuth == undefined || (route.requiresAuth && userLoggedIn)
"
>
<li class="navigation-link" :class="{ active: route.route == active }">
<component class="navigation-icon" :is="route.icon"></component>
<span>{{ route.title }}</span>
</li>
</router-link>
</template>
<script>
export default {
name: "NavigationIcons",
props: {
active: {
type: String,
required: false
},
route: {
type: Object,
required: true
}
},
computed: {
userLoggedIn() {
return localStorage.getItem("token") ? true : false;
}
}
};
</script>
<style lang="scss" scoped>
@import "./src/scss/media-queries";
.navigation-link {
display: grid;
place-items: center;
height: var(--header-size);
list-style: none;
padding: 1rem 0.15rem;
margin: 0;
text-align: center;
background-color: var(--background-color-secondary);
transition: transform 0.3s ease, color 0.3s ease, stoke 0.3s ease,
fill 0.3s ease, background-color 0.5s ease;
@include mobile {
height: 90px;
padding: 1rem;
width: 50vw;
}
&:hover {
transform: scale(1.05);
}
&:hover,
&.active {
background-color: var(--background-color);
span,
.navigation-icon {
color: var(--text-color);
stroke: var(--text-color);
stroke-width: 2px;
}
}
span {
text-transform: uppercase;
font-size: 11px;
color: var(--text-color-70);
}
}
a {
text-decoration: none;
}
.navigation-icon {
width: 28px;
stroke: var(--text-color-70);
transition: inherit;
}
</style>