Made responsive menu and dropdown

This commit is contained in:
Vivek Santayana 2022-08-28 10:20:26 +01:00
parent fd1c4ee14d
commit c63ace6f08
5 changed files with 104 additions and 31 deletions

View File

@ -0,0 +1,8 @@
const menuButton = document.getElementById('menu-btn')
const mobileNav = document.getElementById('mobile-nav')
menuButton.addEventListener('click', ()=> {
menuButton.classList.toggle('menu-active')
mobileNav.classList.toggle('flex')
mobileNav.classList.toggle('hidden')
})

View File

@ -1,19 +1,30 @@
<script> <script>
import { mdiAccountQuestion, mdiHome, mdiInformation } from '@mdi/js'
import mdiMenu from '@/components/icons/Menu.vue' import mdiMenu from '@/components/icons/Menu.vue'
import MobileLink from '@/components/navbar/MobileLink.vue'
import Navlink from '@/components/navbar/Navlink.vue' import Navlink from '@/components/navbar/Navlink.vue'
export default { export default {
name: 'Navbar', name: 'Navbar',
components: { components: {
mdiMenu, mdiMenu,
MobileLink,
Navlink Navlink
},
data() {
return {
menuActive: false
}
},
methods: {
toggleMenu() {
this.menuActive = !this.menuActive
}
} }
} }
</script> </script>
<template> <template>
<nav class="fixed bg-lime-900 text-white shadow-xl w-full"> <nav class="fixed bg-lime-900 text-white shadow-xl w-full z-50">
<div class="flex relative px-2 max-w-5xl mx-auto items-center justify-between"> <div class="flex relative px-2 max-w-5xl mx-auto items-center justify-between z-50">
<!-- Logo --> <!-- Logo -->
<div class="inline-flex justify-center items-center"> <div class="inline-flex justify-center items-center">
<img src="@/assets/vue.svg" class="p-2" alt="Vite Logo" > <img src="@/assets/vue.svg" class="p-2" alt="Vite Logo" >
@ -23,16 +34,68 @@
<div class="hidden md:flex space-x-3 justify-center items-center"> <div class="hidden md:flex space-x-3 justify-center items-center">
<Navlink :svgPath="route.meta.svgPath" :text="route.meta.title" :to="route.path" v-for="route in $router.options.routes" :key="route.path" /> <Navlink :svgPath="route.meta.svgPath" :text="route.meta.title" :to="route.path" v-for="route in $router.options.routes" :key="route.path" />
</div> </div>
<button id="menu-btn" class="block md:hidden focus:outline-none"> <button id="menu-btn" title="Toggle Menu" aria-title="Toggle Menu" class="block focus:ring relative menu-toggle md:hidden focus:outline-none" :class="{'menu-active': menuActive}" @click="toggleMenu">
<mdiMenu /> <span class="menu-toggle-top"></span>
<span class="menu-toggle-middle"></span>
<span class="menu-toggle-bottom"></span>
</button> </button>
</div> </div>
<div class="hidden md:hidden"> <Transition
<div id="menu" class="absolute flex flex-col self-end font-bold sm:w-auto drop-shadow-md"> enter-active-class="transition-all duration-200 ease-in"
<Navlink :svgPath="route.meta.svgPath" :text="route.meta.title" :to="route.path" v-for="route in $router.options.routes" :key="route.path" /> enter-from-class="opacity-0 -translate-y-1/3"
</div> leave-active-class="transition-all duration-200 ease-out"
leave-to-class="opacity-0 -translate-y-1/3"
>
<div id="mobile-nav" class="z-0 w-full bg-lime-700 p-2 space-y-1 absolute flex-col self-end self-center font-bold drop-shadow-md flex md:hidden" v-if="menuActive">
<MobileLink :svgPath="route.meta.svgPath" :text="route.meta.title" :to="route.path" v-for="route in $router.options.routes" :key="route.path" @optionSelected="toggleMenu" />
</div> </div>
</Transition>
</nav> </nav>
</template> </template>
<style scoped> <style scoped>
.menu-toggle {
cursor: pointer;
width: 24px;
height: 24px;
transition: all 0.3s;
position: relative;
}
.menu-toggle-top, .menu-toggle-middle, .menu-toggle-bottom {
position: absolute;
top: 0;
left: 0;
width: 24px;
height: 2px;
@apply bg-white;
transform: rotate(0);
transition: all 0.5s;
}
.menu-toggle-top {
transform: translateY(4px);
}
.menu-toggle-middle {
transform: translateY(11px);
}
.menu-toggle-bottom {
transform: translateY(18px);
}
.menu-active {
transform: rotate(90deg) translateY(0px);
}
.menu-active .menu-toggle-top {
transform: rotate(45deg) translateX(6px) translateY(6px);
}
.menu-active .menu-toggle-middle {
display: none;
}
.menu-active .menu-toggle-bottom {
transform: rotate(-45deg) translateX(-6px) translateY(6px);
}
</style> </style>

View File

@ -6,36 +6,39 @@ export default {
components: { components: {
SvgIcon SvgIcon
}, },
methods: {
optionSelected() {
this.$emit('optionSelected')
}
},
props: [ props: [
"path", "svgPath",
"text", "text",
"to" "to"
] ]
} }
</script> </script>
<template> <template>
<a :href="to" class="navlink" target="_blank" rel="noopener noreferrer" v-if="to.startsWith('http')"> <a :href="to" class="navlink w-full" target="_blank" rel="noopener noreferrer" v-if="to.startsWith('http')" v-on:click="optionSelected">
<div class="inline-flex px-3 py-1"> <div class="inline-flex px-3 py-1 space-x-1">
<span class="scale-75"> <span class="scale-75">
<svg-icon type="mdi" :path="path"></svg-icon> <svg-icon type="mdi" :path="svgPath"></svg-icon>
</span> </span>
<span>{{ text }}</span> <span>{{ text }}</span>
</div> </div>
</a> </a>
<div class="relative flex" v-else> <div class="relative flex" v-else>
<div class="relative flex"> <router-link active-class="active-link" class="navlink w-full" :to="to" v-on:click="optionSelected">
<router-link active-class="active-link" class="navlink" :to="to"> <div class="inline-flex px-3 py-1 space-x-1">
<div class="inline-flex px-3 py-1">
<span class="scale-75"> <span class="scale-75">
<svg-icon type="mdi" :path="path"></svg-icon> <svg-icon type="mdi" :path="svgPath"></svg-icon>
</span> </span>
<span>{{ text }}</span> <span>{{ text }}</span>
</div> </div>
</router-link> </router-link>
</div> </div>
</div>
</template> </template>
<style> <style scoped>
.navlink.active-link, .navlink.exact-active-link { .navlink.active-link, .navlink.exact-active-link {
@apply bg-orange-500; @apply bg-orange-500;
@apply text-white; @apply text-white;
@ -44,7 +47,6 @@ export default {
} }
.navlink { .navlink {
@apply hover:text-orange-300; @apply hover:text-orange-300;
@apply bg-lime-800;
@apply active:text-orange-500; @apply active:text-orange-500;
@apply rounded-lg; @apply rounded-lg;
@apply h-fit; @apply h-fit;

View File

@ -102,12 +102,12 @@ Router.beforeEach((to, from, next) => {
Router.afterEach( (to, from) => { Router.afterEach( (to, from) => {
const enterActiveClass = 'transition-all duration-300 ease-in' const enterActiveClass = 'transition-all duration-300 ease-in'
const leaveActiveClass = 'transition-all duration-300 ease-out' const leaveActiveClass = 'transition-all duration-300 ease-out'
const baseFromClass = 'transform opacity-0 scale-75' const baseFromClass = 'transform opacity-0 md:scale-75'
if ( to.fullPath.split('/')[1] !== from.fullPath.split('/')[1] || !from.matched.length ) { if ( to.fullPath.split('/')[1] !== from.fullPath.split('/')[1] || !from.matched.length ) {
const fromIndex = from.meta.parentIndex ? from.meta.parentIndex : from.meta.index const fromIndex = from.meta.parentIndex ? from.meta.parentIndex : from.meta.index
const toIndex = to.meta.parentIndex ? to.meta.parentIndex : to.meta.index const toIndex = to.meta.parentIndex ? to.meta.parentIndex : to.meta.index
const slideInDirection = toIndex < fromIndex ? '-translate-x-1/3' : 'translate-x-1/3' const slideInDirection = toIndex < fromIndex ? 'md:-translate-x-1/3' : 'md:translate-x-1/3'
const slideOutDirection = toIndex < fromIndex ? 'translate-x-1/3' : '-translate-x-1/3' const slideOutDirection = toIndex < fromIndex ? 'md:translate-x-1/3' : 'md:-translate-x-1/3'
to.meta.mainEnterActiveClass = enterActiveClass to.meta.mainEnterActiveClass = enterActiveClass
to.meta.mainLeaveActiveClass = leaveActiveClass to.meta.mainLeaveActiveClass = leaveActiveClass
to.meta.mainEnterFromClass = baseFromClass + ' ' + slideInDirection to.meta.mainEnterFromClass = baseFromClass + ' ' + slideInDirection
@ -115,8 +115,8 @@ Router.afterEach( (to, from) => {
} else if ( to.fullPath.split('/')[1] == from.fullPath.split('/')[1] && from.matched.length ) { } else if ( to.fullPath.split('/')[1] == from.fullPath.split('/')[1] && from.matched.length ) {
const fromIndex = from.meta.index const fromIndex = from.meta.index
const toIndex = to.meta.index const toIndex = to.meta.index
const slideInDirection = toIndex < fromIndex ? '-translate-y-1/3' : 'translate-y-1/3' const slideInDirection = toIndex < fromIndex ? 'md:-translate-y-1/3' : 'md:translate-y-1/3'
const slideOutDirection = toIndex < fromIndex ? 'translate-y-1/3' : '-translate-y-1/3' const slideOutDirection = toIndex < fromIndex ? 'md:translate-y-1/3' : 'md:-translate-y-1/3'
to.meta.panelEnterActiveClass = enterActiveClass to.meta.panelEnterActiveClass = enterActiveClass
to.meta.panelLeaveActiveClass = leaveActiveClass to.meta.panelLeaveActiveClass = leaveActiveClass
to.meta.panelEnterFromClass = baseFromClass + ' ' + slideInDirection to.meta.panelEnterFromClass = baseFromClass + ' ' + slideInDirection

View File

@ -10,7 +10,7 @@
</script> </script>
<template> <template>
<div> <div>
<div class="flex flex-col md:flex-row space-x-6"> <div class="flex flex-col space-y-3 md:flex-row md:space-x-6 md:space-y-0">
<div class="flex flex-col w-full md:w-1/3 h-fit"> <div class="flex flex-col w-full md:w-1/3 h-fit">
<Sidebar :prefix="$router.options.routes[2].path" :routes="$router.options.routes[2].children"> <Sidebar :prefix="$router.options.routes[2].path" :routes="$router.options.routes[2].children">
<template v-slot:header> <template v-slot:header>