How to create menu with sub-menu with Vuetify?

Viewed 6968

I need to create a menu with icon button. If a button supposed to have a menu - clicking on it should open this menu, otherwise perform a certain action. If any of the menu items have a sub-menu - clicking on should open the sub-menu. Basically it should look something like that (sorry, drawn with Paint):

enter image description here

Now I have buttons data with the following structure:

buttons: [
{
  id: 'options',
  title: 'More Options',
  icon: 'fas fa-bars',
  action: '',
  options: [
    {id: 'dictionary', title: 'Dictionary', action: ''},
    {id: 'visualization', title: 'Data Visualization', action: ''},
    {id: 'password', title: 'Change Password', action: ''},
    {id: 'license', title: 'License Info', action: ''},
    {
      id: 'tools', title: 'Tools', action: '',
      options: [
        {id: 'calculator', title: 'Hex to ASCII calculator'}
      ]
    },
    {id: 'checkup', title: 'Checkup Report', action: ''},
    {id: 'system', title: 'System Monitoring', action: ''},
    {id: 'db', title: 'Database Management', action: ''},
  ]
},
{id: 'reports', title: 'Reports', icon: 'fas fa-chart-line', action: ''},
{
  id: 'help',
  title: 'Help Options',
  icon: 'fas fa-question-circle',
  action: '',
  options: [
    {id: 'user', title: 'User Guide', action: ''},
    {id: 'admin', title: 'Admin Guide', action: ''},
  ]
},
{id: 'settings', title: 'Settings', icon: 'fas fa-cog', action: ''},
{id: 'logout', title: 'Logout', icon: 'fas fa-power-off', action: ''},
]

And I tried to create the menu with the following code:

<v-menu v-for='button in $store.state.topbar.buttons'>
      <template #activator="{ on: menu }" v-show='button.options'>
        <v-tooltip bottom >
          <template #activator="{ on: tooltip }">
            <v-btn
              color="info"
              icon small flat
              v-on="{ ...tooltip, ...menu }"
            >
              <v-icon>{{button.icon}}</v-icon>
            </v-btn>
          </template>
          <span>{{button.title}}</span>
        </v-tooltip>
      </template>
      <temmplate  #activator="{ on: tooltip }" v-show='!button.options'>
        <v-tooltip bottom >
          <template #activator="{ on: tooltip }">
            <v-btn
              color="info"
              icon small flat
              v-on="{ ...tooltip }"
            >
              <v-icon>{{button.icon}}</v-icon>
            </v-btn>
          </template>
          <span>{{button.title}}</span>
        </v-tooltip>
      </temmplate>
      <v-list v-show='button.options'>
        <v-list-tile
          v-for="(item, index) in button.options"
          :key="item.id"
          @click=""
        >
          <v-list-tile-content>
            <v-list-tile-title v-html="item.title"></v-list-tile-title>
          </v-list-tile-content>
          <v-list-tile-action v-if='item.options'>
            <v-icon color='info' small>fa-chevron-right</v-icon>
          </v-list-tile-action>

          <v-menu offset-y
            <template v-slot:activator="{ on }">
              <v-list-tile-content>
                <v-list-tile-title v-html="item.title"></v-list-tile-title>
              </v-list-tile-content>
              <v-list-tile-action v-if='item.options'>
                <v-icon color='info' small>fa-chevron-right</v-icon>
              </v-list-tile-action>
            </template>
            <v-list>
              <v-list-tile
                v-for="(submenu, index) in  item.options"
                :key="index"
                @click=""
              >
                <v-list-tile-title>{{ submenu.title }}</v-list-tile-title>
              </v-list-tile>
            </v-list>
          </v-menu>

And I'm getting this result:

enter image description here

So the sub-menu sort of becomes part of the menu. No matter what I tried - either I don't see the sub-menu at all, or see it as in the picture.

I couldn't find any documentation for sub-menu on Vuetify site, tried to follow APIs of menu, list, button, but wasn't able to do it as I want it to appear. Can it be done? And if yes - how?

EDIT

I tried to recreate the issue in CodePen, but it came out very weird looking...

1 Answers

You can achieve this using tricks try this. https://codepen.io/anon/pen/yWdBoG?editors=1010

<div id="app">
  <v-app id='menu-app'>
    <v-toolbar light dense color='white' class='top-toolbar'>
      <v-menu v-for='button in buttons' :close-on-content-click="content_click_option">
          <template #activator="{ on: menu }" v-show='button.options'>
            <v-tooltip bottom >
              <template #activator="{ on: tooltip }">
                <v-btn
                  color="info"
                  icon small flat
                  v-on="{ ...tooltip, ...menu }"
                >
                  <v-icon>{{button.icon}}</v-icon>
                </v-btn>
              </template>
              <span>{{button.title}}</span>
            </v-tooltip>
          </template>
          <temmplate v-if="content_click_option"  #activator="{ on: tooltip }" v-show='!button.options'>
            <v-tooltip bottom >
              <template #activator="{ on: tooltip }">
                <v-btn
                  color="info"
                  icon small flat
                  v-on="{ ...tooltip }"
                >
                  <v-icon>{{button.icon}}</v-icon>
                </v-btn>
              </template>
              <span>{{button.title}}</span>
            </v-tooltip>
          </temmplate>
          <v-list v-show='button.options'>
            <v-list-tile
              v-for="(item, index) in button.options"
              :key="item.id"
              @click="item.options?content_click_option=false:content_click_option=true"
            >
              <v-list-tile-content>
                <v-list-tile-title v-html="item.title"></v-list-tile-title>

              </v-list-tile-content>
              <v-list-tile-action v-if='item.options'>

                 <v-menu 
        v-model="menu"
        :close-on-content-click="false"
        :nudge-width="200"
        offset-x
      >
        <template v-slot:activator="{ on }">

          <v-icon v-on="on" color='info' small @click="menu=true">fa-chevron-right</v-icon>
        </template>

        <v-card>
          <v-list>
            <v-list-tile avatar>
              <v-list-tile-avatar>
                <img src="https://cdn.vuetifyjs.com/images/john.jpg" alt="John">
              </v-list-tile-avatar>

              <v-list-tile-content>
                <v-list-tile-title>John Leider</v-list-tile-title>
                <v-list-tile-sub-title>Founder of Vuetify.js</v-list-tile-sub-title>
              </v-list-tile-content>

              <v-list-tile-action>
                <v-btn
                  :class="fav ? 'red--text' : ''"
                  icon
                  @click="fav = !fav"
                >
                  <v-icon>favorite</v-icon>
                </v-btn>
              </v-list-tile-action>
            </v-list-tile>
          </v-list>

          <v-divider></v-divider>

          <v-list>
            <v-list-tile>
              <v-list-tile-action>
                <v-switch v-model="message" color="purple"></v-switch>
              </v-list-tile-action>
              <v-list-tile-title>Enable messages</v-list-tile-title>
            </v-list-tile>

            <v-list-tile>
              <v-list-tile-action>
                <v-switch v-model="hints" color="purple"></v-switch>
              </v-list-tile-action>
              <v-list-tile-title>Enable hints</v-list-tile-title>
            </v-list-tile>
          </v-list>

          <v-card-actions>
            <v-spacer></v-spacer>

            <v-btn flat @click="menu = false">Cancel</v-btn>
            <v-btn color="primary" flat @click="menu = false;">Save</v-btn>
          </v-card-actions>
        </v-card>
      </v-menu>
              </v-list-tile-action>


            </v-list-tile>
          </v-list>
        </v-menu>

    </v-toolbar>
  </v-app>
</div>


data: {
     fav: true,
    menu: false,
    message: false,
    hints: true,
    content_click_option:false,
    buttons: [
    {
      id: 'options',
      title: 'More Options',
      icon: 'fas fa-bars',
      action: '',
      options: [
        {id: 'dictionary', title: 'Dictionary', action: ''},
        {id: 'visualization', title: 'Data Visualization', action: ''},
        {id: 'password', title: 'Change Password', action: ''},
        {id: 'license', title: 'License Info', action: ''},
        {
          id: 'tools', title: 'Tools', action: '',
          options: [
            {id: 'calculator', title: 'Hex to ASCII calculator'}
          ]
        },
        {id: 'checkup', title: 'Checkup Report', action: ''},
        {id: 'system', title: 'System Monitoring', action: ''},
        {id: 'db', title: 'Database Management', action: ''},
      ]
    },
    {id: 'reports', title: 'Reports', icon: 'fas fa-chart-line', action: ''},
    {
      id: 'help',
      title: 'Help Options',
      icon: 'fas fa-question-circle',
      action: '',
      options: [
        {id: 'user', title: 'User Guide', action: ''},
        {id: 'admin', title: 'Admin Guide', action: ''},
      ]
    },
    {id: 'settings', title: 'Settings', icon: 'fas fa-cog', action: ''},
    {id: 'logout', title: 'Logout', icon: 'fas fa-power-off', action: ''},
  ]
  }
Related