Made responsive menu and dropdown
This commit is contained in:
		
							
								
								
									
										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>
 | 
			
		||||
    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" />
 | 
			
		||||
        <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>
 | 
			
		||||
        </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>
 | 
			
		||||
@@ -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">
 | 
			
		||||
                    <span class="scale-75">
 | 
			
		||||
                        <svg-icon type="mdi" :path="path"></svg-icon>
 | 
			
		||||
                    </span>
 | 
			
		||||
                    <span>{{ text }}</span>
 | 
			
		||||
                </div>
 | 
			
		||||
            </router-link>
 | 
			
		||||
        </div>
 | 
			
		||||
        <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="svgPath"></svg-icon>
 | 
			
		||||
                </span>
 | 
			
		||||
                <span>{{ text }}</span>
 | 
			
		||||
            </div>
 | 
			
		||||
        </router-link>
 | 
			
		||||
    </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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user