A simple hands-on mobile nested menu UI component with a smooth slide animation

Mobile applications often require intuitive and visually appealing navigation systems to enhance the user experience. In this hands-on guide, we’ll explore the creation of a simple yet effective Vue.js mobile nested menu UI component. The component will feature smooth slide animations, providing users with an engaging and seamless navigation experience.

Setting Up the Vue Project:

Begin by creating a new Vue project using the Vue CLI. If you haven’t installed it, run:

npm install -g @vue/cli

Create a new project:

vue create mobile-nested-menu
cd mobile-nested-menu

Designing the NestedMenu Component:

  1. Create the NestedMenu.vue component:
<template>
  <div class="nested-menu" :class="{ 'menu-open': isMenuOpen }">
    <div class="menu-header" @click="toggleMenu">
      <span>{{ isMenuOpen ? 'Close Menu' : 'Open Menu' }}</span>
    </div>
    <transition name="slide">
      <div v-if="isMenuOpen" class="menu-content">
        <ul>
          <li v-for="category in menuData" :key="category.id" @click="selectCategory(category)">
            {{ category.name }}
            <ul v-if="category.subcategories.length > 0">
              <li v-for="subcategory in category.subcategories" :key="subcategory.id" @click="selectSubcategory(subcategory)">
                {{ subcategory.name }}
              </li>
            </ul>
          </li>
        </ul>
      </div>
    </transition>
  </div>
</template>

<script>
export default {
  data() {
    return {
      isMenuOpen: false,
      menuData: [
        { id: 1, name: 'Category 1', subcategories: [{ id: 11, name: 'Subcategory 1.1' }, { id: 12, name: 'Subcategory 1.2' }] },
        { id: 2, name: 'Category 2', subcategories: [] },
        { id: 3, name: 'Category 3', subcategories: [{ id: 31, name: 'Subcategory 3.1' }] },
      ],
    };
  },
  methods: {
    toggleMenu() {
      this.isMenuOpen = !this.isMenuOpen;
    },
    selectCategory(category) {
      console.log(`Selected Category: ${category.name}`);
    },
    selectSubcategory(subcategory) {
      console.log(`Selected Subcategory: ${subcategory.name}`);
      // Additional logic, e.g., navigate to a page or perform an action
    },
  },
};
</script>

<style scoped>
.nested-menu {
  position: relative;
  width: 100%;
}

.menu-header {
  background-color: #3498db;
  color: #fff;
  padding: 10px;
  text-align: center;
  cursor: pointer;
}

.menu-content {
  background-color: #f4f4f4;
  overflow: hidden;
}

.menu-content ul {
  list-style: none;
  padding: 0;
}

.menu-content li {
  padding: 10px;
  cursor: pointer;
}

.menu-content li:hover {
  background-color: #e0e0e0;
}

.slide-enter-active, .slide-leave-active {
  transition: transform 0.5s;
}

.slide-enter, .slide-leave-to {
  transform: translateX(-100%);
}
</style>

Using the NestedMenu Component in App.vue:

<template>
  <div id="app">
    <NestedMenu />
  </div>
</template>

<script>
import NestedMenu from './components/NestedMenu.vue';

export default {
  components: {
    NestedMenu,
  },
};
</script>

<style>
#app {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background-color: #ecf0f1;
  margin: 0;
}
</style>

Explanation of the NestedMenu Component:

  1. The template section includes a header to toggle the menu visibility and a transition-wrapped menu content with nested categories and subcategories.
  2. The script section defines data for menu states and structures, as well as methods to handle menu interactions.
  3. The scoped styles define the appearance and smooth slide animation for the nested menu.

Running the Application:

Run the following commands to see the nested menu in action:

npm install
npm run serve

Visit http://localhost:8080 in your browser to interact with the mobile nested menu component.

Complete implementation of a simple hands-on mobile nested menu UI component with a smooth slide animation

vue-nested-menu

A simple hands-on mobile nested menu UI component with a smooth slide animation.

Installation

Yarn / NPM

$ yarn add vue-nested-menu

main.js

import VueNestedMenu from 'vue-nested-menu';

Vue.use(VueNestedMenu);

Usage

Basic

index.html

<div id="app">
  <vue-nested-menu :source="menu"></vue-nested-menu>
</div>

main.js

import VueNestedMenu from 'vue-nested-menu';

Vue.use(VueNestedMenu)

new Vue({
  el: '#app',
  data: {
    menu: {
      title: '首頁',
      children: [
        {
          title: `Today's Deals`,
          link: `/today`,
          children: [],
        },
        {
          title: `Shop By Department`,
          children: [
            {
              title: `Amazon Music`,
              link: `/music`,
              children: [],
            },
            {
              title: `CDs and Vinyl`,
              link: `/cds`,
              children: [],
            },
          ],
        },
      ],
    },
  },
});

Single File Component

app.js

import VueNestedMenu from 'vue-nested-menu';

Vue.use(VueNestedMenu);

// ...

MyMenu.vue

<template>
  <vue-nested-menu :source="menu" />
</template>

<script>
export default {
  data() {
    return {
      menu: {
        // your menu data
      },
    };
  },
};
</script>

Styling

You can use following classes for your own customizations

default style

.Menu__header {
    display: flex;
    align-items: center;
    padding-left: 35px;
    height: 50px;
    color: #fff;
    font-size: 16px;
    background-color: #232f3e;
    cursor: pointer;

    .arrow {
        padding-top: 2px;
        fill: #fff;
        margin-right: 10px;
        width: 10px;
        height: 100%;
        display: flex;
        align-items: center;
    }
}

.Menu__list {
    list-style: none;
    padding-bottom: 2px;

    .separator {
        border-bottom: 1px solid #d5dbdb;
        padding: 2px 0 0 0;
        margin: 0;
    }
}

.Menu__item {
    color: #4a4a4a;
    padding-left: 35px;
    height: 45px;
    display: flex;
    align-items: center;
    cursor: pointer;

    a {
        color: #4a4a4a;
        text-decoration: none;
    }

    .arrow {
        padding-top: 2px;
        padding-left: 15px;
        display: flex;
        align-items: center;
        width: 10px;
        height: 100%;
    }
}

Leave a Comment