Made responsive menu and dropdown
This commit is contained in:
parent
fd1c4ee14d
commit
c63ace6f08
8
client/src/assets/js/menu-toggle.js
Normal file
8
client/src/assets/js/menu-toggle.js
Normal 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')
|
||||||
|
})
|
@ -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"
|
||||||
|
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>
|
||||||
</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>
|
@ -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="svgPath"></svg-icon>
|
||||||
<svg-icon type="mdi" :path="path"></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;
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user