Keep v-menu open state when the activator is pressed again - vuejs2

I'm trying to make a custom v-autocomplete using v-text-field and v-menu.
What confuses me is that v-menu closes when you click on the v-text-field again. The first click on the v-text field opens the v-menu, and the second click closes it. I couldn't find any property that turns off toggle mode.
Although, if you look at the html markup, v-menu is used in the v-select and v-autocomplete components, which close when you click on the content or outside the menu.
Codepen
<template>
<div id="app">
<v-app id="inspire">
<div class="text-center">
<v-main>
<v-container>
<v-row>
<v-col cols="3">
<v-menu bottom offset-y max-height="304px">
<template v-slot:activator="{ on, attrs }">
<v-text-field v-model="model && model.title"
dense color="primary"
v-bind="attrs" v-on="on"
label="Choose" outlined>
</v-text-field>
</template>
<v-list dense>
<v-list-item-group v-model="model"
color="primary">
<v-list-item v-for="(item, index) in items"
:key="item.id" :value="item"
dense>
<v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item>
</v-list-item-group>
</v-list>
</v-menu>
</v-col>
</v-row>
</v-container>
</v-main>
</div>
</v-app>
</div>
</template>
<script>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
items: [
{id:1, title: 'Click Me 1' },
{id:2, title: 'Click Me 2' },
{id:3, title: 'Click Me 3' },
{id:4, title: 'Click Me 4' },
],
model: null
}),
})
</script>

Related

Open a dropdown with right click in Vue3 and Vuetify3

I am trying to work on the context menu for Vue3. I want to show the list of items under the v-menu by right-clicking on a dropdown button. I am working on this link as a reference but when I tried to do the same in my Vue3 file, It gives me an error like this-
Can anyone suggest what am I doing wrong? Here is the code-
<v-menu offset-y>
<template v-slot:activator="{ props }">
<v-btn
color="primary"
dark
v-bind="props"
#contextmenu.prevent="props.click"
>
Dropdown
</v-btn>
</template>
<v-list>
<v-list-item
v-for="(item, index) in menuClick"
:key="index"
#click=""
>
<v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
I couldn't discover any context menu event in Vue3's doc and Vuetify's menu API. Although, I have an alternative in my mind that can do this job. You can utilize the .right modifier sync with v-model to open the menu with the right click of a button.
Here is the strategy-
Use v-model to control the v-menu toggling status.
On the right-click, set the v-model to true so the menu will be open.
On the left-click (default) set the v-model to false. (Do this step only if you don't want to open the menu on the default click.)
Here is a functioning demo-
const { createApp } = Vue
const { createVuetify } = Vuetify
const vuetify = createVuetify()
const app = createApp({
data: () => ({
menu: false,
items: [
{ title: 'Click Me' },
{ title: 'Click Me' },
{ title: 'Click Me' },
{ title: 'Click Me 2' },
],
}),
}).use(vuetify).mount('#app')
<script src="https://unpkg.com/vue#3/dist/vue.global.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#3.1.2/dist/vuetify.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vuetify#3.1.2/dist/vuetify.min.css">
<div id="app">
<v-app>
<div class="d-flex justify-space-around mb-5 font-weight-bold">
I will open only with the right-click.
</div>
<div class="d-flex justify-space-around">
<v-menu v-model="menu">
<template v-slot:activator="{ props }">
<v-btn
color="primary"
v-bind="props"
#click.left.prevent="menu = false"
#click.right.prevent="menu = true"
>
Dropdown
</v-btn>
</template>
<v-list>
<v-list-item
v-for="(item, index) in items"
:key="index"
:value="index"
>
<v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</div>
</v-app>
</div>

Vuetify: My navigation drawer is positioned over another element (toolbar)

I would like to put my navigation drawer under the toolbar.
I'm trying to achieve something like this :
I am trying to do something similar but all attempts are unsuccessful, at the moment I have the following:
My code:
<template>
<nav>
<v-snackbar v-model="snackbar" :timeout="4000" top color="success">
<span>Awesome! You added a new project.</span>
<v-btn text flat #click="snackbar = false">Close</v-btn>
</v-snackbar>
<v-toolbar app clipped-left >
<v-toolbar-side-icon></v-toolbar-side-icon>
<v-app-bar-nav-icon #click.stop="drawer = !drawer"></v-app-bar-nav-icon>
<v-toolbar-title class="text-uppercase gr ey--text">
<span class="font-weight-light">estudos</span>
<span>vue</span>
</v-toolbar-title>
<v-spacer></v-spacer>
<v-menu offset-y>
<template v-slot:activator="{ on, attrs }">
<v-btn text
color="primary"
dark
v-bind="attrs"
v-on="on"
>
<v-icon left>expand_more</v-icon>
<span>Menu</span>
</v-btn>
</template>
<v-list>
<v-list-item v-for="link in links" :key="link.text" router :to="link.route">
<v-list-item-title>{{link.text}}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
<v-btn text color="grey">
<span>Sign Out</span>
<v-icon right>exit_to_app</v-icon>
</v-btn>
</v-toolbar>
<v-navigation-drawer v-model="drawer" app class="indigo white--text">
<v-app-bar-nav-icon #click.stop="drawer = !drawer"></v-app-bar-nav-icon>
<v-layout column align-center>
<v-flex class="mt-5">
<v-avatar size="90">
<img src="/avatar-64.png">
</v-avatar>
<p class="white-text dubheading mt-1">
Estudos Vue
</p>
</v-flex>
<v-flex class="mt-4 mb-3">
<popup #projectAdded="snackbar=true" />
</v-flex>
</v-layout>
<v-list >
<v-divider></v-divider>
<v-list-item
v-for="link in links"
:key="link.text"
router :to="link.route"
>
<v-list-item-action >
<v-icon class="white--text">{{ link.icon }}</v-icon>
</v-list-item-action>
<v-list-item-content class="white--text">
<v-list-item-title>{{ link.text }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
</v-navigation-drawer>
<v-row>
<v-col
>
<v-img
max-height="76%"
max-width="100%"
src="/imgtest.jpg"
gradient="to top right, rgba(14,12,11,.51), rgba(14,12,11,.71)"
>
<v-img-title class="heading white--text">
Bien saude</v-img-title></v-img>
</v-col>
</v-row>
</nav>
</template>
<script>
import Popup from './Popup'
export default {
components: { Popup },
data() {
return {
drawer:false,
links:[
{icon: 'dashboard', text:'Dashboard', route:'/'},
{icon: 'folder', text:'My Projects', route:'/projects'},
{icon: 'person', text:'Team', route:'/team'},
],
items: [
{ title: 'Click Me' },
{ title: 'Click Me' },
{ title: 'Click Me' },
{ title: 'Click Me 2' },
],
snackbar: true
}
},
}
</script>
I tried adding Block and removing the app, but it didn't solve the problem...
How do I put my drawer under the toolbar?
Add clipped to v-navigation-drawer props like:
<v-navigation-drawer
clipped>
<!-- ... -->
</v-navigation-drawer>
I would suggest following vuetify.js' default markup first.
Then you'll supposed to use clipped property in the <v-navigation-drawer> element like #mamadou-hady-barry already described. (<v-navigation-drawer clipped app>).
Secondly add the 'clipped-left' to the <v-app-bar> element resulting in: <v-app-bar clipped-left app>.
You are currently using <v-toolbar> which does not have a clipped-left property according to their "API".

VueJs Vuetify Autocomplete with links

I'am using vuetify and i want make auto complete like the one on their official site.
But i'm faces some issues :
The items value not appear in the return list and i don't know how to make appear the links.
Here is my code.
Thanks You
<template>
<v-app>
<v-app-bar app color="primary" dark>
<v-app-bar-nav-icon #click="drawer = true"></v-app-bar-nav-icon>
<v-toolbar-title> Board </v-toolbar-title>
<v-spacer></v-spacer>
<v-autocomplete
:loading="loading"
:filter="v => v"
:items="items"
:search-input.sync="search"
v-model="select"
flat hide-no-data hide-details return-object placeholder="Search ...">
<v-list-tile
slot="prepend-item"
class="grey--text">
{{ items.length }} menus found
</v-list-tile>
<template slot="selection" slot-scope="{ item }">
{{item.name}} {{item.url}}
</template>
<template slot="item" slot-scope="{ item }">
<v-list-tile-content>
<p class='fullName'>{{item.name}} {{item.url}}</p>
</v-list-tile-content>
</template>
</v-autocomplete>
</v-app-bar>
<v-main>
<HelloWorld/>
</v-main>
</v-app>
I went through your issue and try to make a codepen and it worked.
My suggestion is that you should consider data structure when you use object with auto complete and it 's needed even you use v-select.
Please check this pen. https://codepen.io/endmaster0809/pen/qBZRywZ
items: [
{
name: 'Alerts',
url: 'https://vuetifyjs.com/en/components/alerts/'
},
{
name: 'Autocompletes',
url: 'https://vuetifyjs.com/en/components/autocompletes/#autocompletes'
}
],
select: {
name: 'Alerts',
url: 'https://vuetifyjs.com/en/components/alerts/'
},

How to "stop" an event?

I'm learning a Vuetifyjs and try writing a "file explorer".
There is an example:
codepen-snippet
I can’t understand how to make sure that when you click on the right icon, the entry in the "tree" does not become "active".
Probably need to “stop” the events, but I don’t know how to do it.
Tell me.
Thank.
I want to click on this menu: Menu
it became like this: need
not like now: now
Snippet:
<div id="app">
<v-app id="inspire">
<v-content>
<v-container >
<v-layout justify-center>
<v-card min-width=400>
<v-treeview
v-model="tree"
:open="open"
:items="items"
activatable
hoverable
item-key="name"
open-on-click
>
<template v-slot:prepend="{ item, open }">
<v-icon v-if="!item.file">
{{ open ? 'mdi-folder-open' : 'mdi-folder' }}
</v-icon>
<v-icon v-else>
{{ files[item.file] }}
</v-icon>
</template>
<template v-slot:label="{item}">
<v-hover v-slot:default="{ hover }">
<div class="d-flex align-center">
<span>{{item.name}}</span>
<v-menu
class="ml-auto"
style="display: inline"
:nudge-width="200"
offset-y
>
<template v-slot:activator="{ on }">
<!--
-->
<v-btn
v-show="hover"
icon
small
v-on="on"
class="pa-0 ma-0"
>
<v-icon small class="pa-0 ma-0">more_vert</v-icon>
</v-btn>
</template>
<v-card>
<v-list>
<v-list-item #click="() => {}">
<v-list-item-action>
<v-icon>mdi-information-variant</v-icon>
</v-list-item-action>
<v-list-item-title>Info</v-list-item-title>
</v-list-item>
<v-list-item v-if="item.type === 'process' || item.type === 'state'" #click="() => {}">
<v-list-item-action>
<v-icon>power_settings_new</v-icon>
</v-list-item-action>
<v-list-item-title>Status</v-list-item-title>
</v-list-item>
<v-list-item #click="() => {}">
<v-list-item-action>
<v-icon>create</v-icon>
</v-list-item-action>
<v-list-item-title>Rename</v-list-item-title>
</v-list-item>
<v-list-item #click="() => {}">
<v-list-item-action>
<v-icon>file_copy</v-icon>
</v-list-item-action>
<v-list-item-title>Copy</v-list-item-title>
</v-list-item>
<v-list-item #click="() => {}">
<v-list-item-action>
<v-icon>mdi-folder-plus</v-icon>
</v-list-item-action>
<v-list-item-title>Create folder</v-list-item-title>
</v-list-item>
<v-list-item #click="() => {}">
<v-list-item-action>
<v-icon>delete</v-icon>
</v-list-item-action>
<v-list-item-title>Delete</v-list-item-title>
</v-list-item>
</v-list>
</v-card>
</v-menu>
</div>
</v-hover>
</template
</v-treeview>
</v-card
</v-layout justify-center>
</v-container>
</v-content>
</v-app>
</div>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
open: ['public'],
files: {
html: 'mdi-language-html5',
js: 'mdi-nodejs',
json: 'mdi-json',
md: 'mdi-markdown',
pdf: 'mdi-file-pdf',
png: 'mdi-file-image',
txt: 'mdi-file-document-outline',
xls: 'mdi-file-excel',
},
tree: [],
items: [
{
name: '.git',
},
{
name: 'node_modules',
},
{
name: 'public',
children: [
{
name: 'static',
children: [{
name: 'logo.png',
file: 'png',
}],
},
{
name: 'favicon.ico',
file: 'png',
},
{
name: 'index.html',
file: 'html',
},
],
},
{
name: '.gitignore',
file: 'txt',
},
{
name: 'babel.config.js',
file: 'js',
},
{
name: 'package.json',
file: 'json',
},
{
name: 'README.md',
file: 'md',
},
{
name: 'vue.config.js',
file: 'js',
},
{
name: 'yarn.lock',
file: 'txt',
},
],
}),
})
What is happening here is that your click event is propagating to its parent element, so when you click on the icon to display the menu it also triggers the click event of your parent element which is the file or folder container.
You can add #click.stop to your v-btn in line 44, like this:
<template v-slot:activator="{ on }">
<!--
-->
<v-btn
v-show="hover"
icon
small
v-on="on"
class="pa-0 ma-0"
#click.stop
>
<v-icon small class="pa-0 ma-0">more_vert</v-icon>
</v-btn>
</template>
This will stop the event from propagating to its parent element, you can try it out here: codepen-snippet
Now when you click the button it will display your menu and won't change the active or inactive state on your files or folders.
To remove the blue highlighting of items when clicked, remove "activatable" from the v-treeview component:
<v-treeview
v-model="tree"
:open="open"
:items="items"
activatable
hoverable
item-key="name"
open-on-click >

How to get values of an item in the loop in Vue?

Problem:
I'm trying to figure out how to pass a value of a clicked item into another component
See codepen: https://codepen.io/anon/pen/zRXBNY
In the codepen menu of an item is triggered by right click
Let's say we have a loop (this iterator component) that's displaying all the items of object items
<v-data-iterator
:items="items"
hide-actions
class="ma-4"
>
<v-flex
slot="item"
slot-scope="props"
xs12 sm6 md4 lg3
>
<v-card #contextmenu="show" class="ma-3 elevation-4">
<v-card-title>
<h4>{{ props.item.name }}</h4>
</v-card-title>
</v-card>
</v-flex>
</v-data-iterator>
And we also have this "menu" component in the same .vue component:
<v-menu
absolute
offset-y
:position-x="x"
:position-y="y"
v-model="showMenu"
>
<v-list>
<p class="white ma-3">menu for item: [TITLE]</p>
<v-list-tile
v-for="item in menuItems"
:key="item.title" #click=""
>
<v-list-tile-title>
{{ item.title }}
</v-list-tile-title>
</v-list-tile>
<v-text-field
class="ma-3"
label="rename">
</v-text-field>
</v-list>
</v-menu>
Question:
How do we trigger the menu component and pass the data of that particular item in it (so that we can do something with it)? As an example I put this menu for item: [TITLE] paragraph there, I don't understand how do we pass the title of the clicked item there?
It seem a bit messy to me (i'm not very familiar with Vuetify), but the first solution that comes to my mind is to pass item object to the handler. So your code goes as:
<v-card #contextmenu="show($event, props.item)" class="ma-3 elevation-4"></v-card>
Then once the event is fired and handler function is called you can store the item you clicked on:
show (e, item) {
this.selectedItem = item;
e.preventDefault()
// ...
Now you can use the item inside the model component as:
<v-list>
<p class="white ma-3">
menu for item: {{selectedItem.name}}
</p>
//...
Full demo:
new Vue({
el: '#app',
methods: {
show (e, item) {
this.selectedItem = item;
e.preventDefault()
this.showMenu = false
this.x = e.clientX
this.y = e.clientY
this.$nextTick(() => {
this.showMenu = true
})
}
},
data: () => ({
x: 0,
y: 0,
selectedItem: {},
showMenu: false,
menuItems: [
{ title: 'copy' },
{ title: 'paste' },
{ title: 'delete' }
],
items: [
{
value: false,
name: 'Frozen Yogurt'
},
{
value: false,
name: 'Ice cream sandwich'
},
{
value: false,
name: 'Eclair'
},
{
value: false,
name: 'Cupcake'
}
]
})
})
<link href="https://unpkg.com/vuetify/dist/vuetify.min.css" rel="stylesheet"/>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify/dist/vuetify.min.js"></script>
<div id="app" >
<v-app id="inner" class="grey lighten-3">
<h1 class="ma-2 ml-4">right click on an item to trigger menu</h1>
<!-- ITEMS ON THE PAGE -->
<v-data-iterator
:items="items"
hide-actions
class="ma-4"
>
<v-flex
slot="item"
slot-scope="props"
xs12 sm6 md4 lg3
>
<v-card #contextmenu="show($event, props.item)" class="ma-3 elevation-4">
<v-card-title>
<h4>{{ props.item.name }}</h4>
</v-card-title>
</v-card>
</v-flex>
</v-data-iterator>
<!-- MENU COMPONENT -->
<v-menu
absolute
offset-y
:position-x="x"
:position-y="y"
v-model="showMenu"
>
<v-list>
<p class="white ma-3">menu for item: {{selectedItem.name}}</p>
<v-list-tile
v-for="item in menuItems"
:key="item.title" #click=""
>
<v-list-tile-title>
{{ item.title }}
</v-list-tile-title>
</v-list-tile>
<v-text-field
class="ma-3"
label="rename">
</v-text-field>
</v-list>
</v-menu>
</v-app>
</div>