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>
import { mdiAccountQuestion, mdiHome, mdiInformation } from '@mdi/js'
import mdiMenu from '@/components/icons/Menu.vue'
import MobileLink from '@/components/navbar/MobileLink.vue'
import Navlink from '@/components/navbar/Navlink.vue'
export default {
name: 'Navbar',
components: {
mdiMenu,
MobileLink,
Navlink
},
data() {
return {
menuActive: false
}
},
methods: {
toggleMenu() {
this.menuActive = !this.menuActive
}
}
}
</script>
<template>
<nav class="fixed bg-lime-900 text-white shadow-xl w-full">
<div class="flex relative px-2 max-w-5xl mx-auto items-center justify-between">
<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 z-50">
<!-- Logo -->
<div class="inline-flex justify-center items-center">
<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">
<Navlink :svgPath="route.meta.svgPath" :text="route.meta.title" :to="route.path" v-for="route in $router.options.routes" :key="route.path" />
</div>
<button id="menu-btn" class="block md:hidden focus:outline-none">
<mdiMenu />
<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">
<span class="menu-toggle-top"></span>
<span class="menu-toggle-middle"></span>
<span class="menu-toggle-bottom"></span>
</button>
</div>
<div class="hidden md:hidden">
<div id="menu" class="absolute flex flex-col self-end font-bold sm:w-auto drop-shadow-md">
<Navlink :svgPath="route.meta.svgPath" :text="route.meta.title" :to="route.path" v-for="route in $router.options.routes" :key="route.path" />
</div>
<Transition
enter-active-class="transition-all duration-200 ease-in"
enter-from-class="opacity-0 -translate-y-1/3"
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>
</Transition>
</nav>
</template>
<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>

View File

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

View File

@ -102,12 +102,12 @@ Router.beforeEach((to, from, next) => {
Router.afterEach( (to, from) => {
const enterActiveClass = 'transition-all duration-300 ease-in'
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 ) {
const fromIndex = from.meta.parentIndex ? from.meta.parentIndex : from.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 slideOutDirection = 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 ? 'md:translate-x-1/3' : 'md:-translate-x-1/3'
to.meta.mainEnterActiveClass = enterActiveClass
to.meta.mainLeaveActiveClass = leaveActiveClass
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 ) {
const fromIndex = from.meta.index
const toIndex = to.meta.index
const slideInDirection = toIndex < fromIndex ? '-translate-y-1/3' : 'translate-y-1/3'
const slideOutDirection = 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 ? 'md:translate-y-1/3' : 'md:-translate-y-1/3'
to.meta.panelEnterActiveClass = enterActiveClass
to.meta.panelLeaveActiveClass = leaveActiveClass
to.meta.panelEnterFromClass = baseFromClass + ' ' + slideInDirection

View File

@ -10,7 +10,7 @@
</script>
<template>
<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">
<Sidebar :prefix="$router.options.routes[2].path" :routes="$router.options.routes[2].children">
<template v-slot:header>