Vuetify VTooltip trigger only on activator click - vue.js

I wanted to trigger Vuetify tooltip VTooltip only when the activator is clicked rather than hovered. I tried to bind it with a variable but still triggered on hover.
methods: {
doCopy(){
// copy logic
this.showCopied = true;
setTimeout(() => {
this.showCopied = false
}, 1000)
}
}
<VTooltip v-model="showCopied">
<template #activator="{ on }">
<VBtn v-on="on" #click="doCopy"> COPY </VBtn>
</template>
</VTooltip>

This is actually more complicated than I expected thanks to some bugs. You should be able to just do <v-tooltip :open-on-hover="false">, but a focus listener is still added which causes the click to close the tooltip immediately. Instead you need to bind the click and blur events separately, and add retain-focus-on-click to the button so it doesn't blur immediately.
Full solution:
<v-tooltip bottom :open-on-hover="false">
<template #activator="{ on }">
<v-btn #click="on.click" #blur="on.blur" retain-focus-on-click>Copy</v-btn>
</template>
<span>Copy</span>
</v-tooltip>

It turns out I have to disable the default event handler of the activator.
Simply removing default event object (on) binding solves the issue.
<VTooltip v-model="showCopied">
<template #activator={}>
<VBtn #click="doCopy"> COPY </VBtn>
</template>
</VTooltip>
[UPDATED] based on #Kael Watts-Deuchar answer
NB: the v-model biding is mandatory

In vuetify 2.6.1 you can do it like this now
<v-tooltip
open-on-click
:open-on-hover="false"
>
<template
v-slot:activator="{ on }"
>
<v-btn
v-on="on"
>
button with tooltip
</v-btn>
</template>
<span>tooltip message</span>
</v-tooltip>

Related

How to trigger Vuetify component activator on arbitrary element click?

I'm trying to open a little popup floating menu when an element is clicked with Vuetify (2.5.0) and Vue (2.6.12) e.g.
<v-menu bottom offset-y>
<template v-slot:activator="{ on, attrs }">
<div v-bind="attrs" v-on="on"></div>
</template>
<div>My popup floating content..</div>
</v-menu>
...but I'm not sure how the activator should work with the click event. I'm not using v-btn as the activator for a reason. The vuetify docs give examples, but they always use v-btn e.g. instead of the div in the activator slot above, it's <v-btn v-bind="attrs" v-on="on">A Menu</v-btn>.
You could destruct the on slot prop to get the click event and then use it in your div :
<v-menu bottom offset-y>
<template v-slot:activator="{ on:{click}, attrs }">
<div v-bind="attrs" #click="click">show menu</div>
</template>
<div>My popup floating content..</div>
</v-menu>

Dialog inside autocomplete - vuetify

I am trying to put a dialog inside an autocomplete component
<v-autocomplete v-model="item.selected_item"
:items="items"
item-text="name"
return-object
single-line
#change="setItemId(item)"
>
<div slot="prepend-item">
<add-item></add-item>
</div>
</v-autocomplete>
the code for component <add-item>
<v-dialog persistent v-model="dialog" width="500">
<template v-slot:activator="{ on, attrs }">
<v-btn text block v-bind="attrs" v-on="on">{{__("Add Item")}}</v-btn>
</template>
<v-card>
</v-card>
</v-dialog>
now , the dialog button actually shows up inside the autocomplete , but the problem is that the dialog automatically goes of on any mouse click ,
I noticed that this is related somehow to the autocomplete component , I tried to put the <add-item> component outside of the autocomplete and it worked fine .
how can this be solved ?
Instead of putting the whole <add-item/> inside the autocomplete, you can separate the button and the dialog itself, then control the dialog whenever the button is clicked. We can base our solution at this example. We will declare a variable dialog that will control our add-item dialog's visibility. That variable will be handled by our button inside the autocomplete.
Main Component:
<v-autocomplete
...
>
<div slot="prepend-item">
<v-btn text block #click="dialog = true">{{"Add Item"}}</v-btn>
</div>
</v-autocomplete>
<add-item v-model="dialog"/> <!-- Pass the dialog variable to the add-item component -->
export default {
data: () => ({
items: [...],
dialog: false // let's close the dialog initially.
})
}
Add Item Component:
<v-dialog persistent :value="value" width="500">
<v-card>
...
<!-- To close the dialog, we'll need to use $emit() to
let our parent know that the value had changed. -->
<v-btn #click="$emit('input', false)">Close Dialog</v-btn>
</v-card>
</v-dialog>
export default {
props: ["value"], // Pass the value of the dialog from the main component
};
Notice that we used $emit() when we want to close our dialog. We need to use that because the variable that is controlling the dialog is from the main component, and the best way to change the value from add-item is by emitting a value. More explanation here.
Here is a demo.

how to handle a dropdown visibility manually instead of Vuetify controls this

I have created a dropdown menu in vuetify which looks like this:
[![enter image description here][1]][1]
`
<v-menu offset-y>
<template v-slot:activator="{ on }">
<v-btn text v-on="on">
Details
</v-btn>
</template>
<v-list>
<v-list-item>
<v-form ref="form">
<v-radio-group v-model="metrics" required>
<v-radio
label="ABC"
value="abc"
></v-radio>
<v-radio label="XYZ" value="xyz"></v-radio>
</v-radio-group>
<v-divider></v-divider>
<v-radio-group v-model="order" required>
<v-radio
label="Higher"
value="higher"
></v-radio>
<v-radio
label="Lower"
value="lower"
></v-radio>
</v-radio-group>
<v-divider></v-divider>
<v-btn
#click="
sortTableData(
metrics,
order,
$props.tableItems
)
"
>
Apply
</v-btn>
</v-form>
</v-list-item>
</v-list>
</v-menu>`
however,
when I click the Dropdown and select for eg:Installs, the menu
closes..
I have to click the Dropdown again to choose higher/lower..
and the menu closes again..
And again I have to click the Dropdown to Click "Apply" button.
Question : Is there Any way I can hold this menu until I click "Apply"?
Thanks a lot in advance for your help !
Ok, the idea here is to manually handle the dropdown visibility instead of letting Vuetify controls this.
To do so, you need to:
add a :close-on-content-click="false" on your <v-menu> (doc)
add a v-model directive binded to a "data" boolean value (ex: v-model="isDropdownDisplayed"), initialized to false (closed dropdown at load)
The first prop tells Vuetify to not close the dropdown when clicking on content (only an outside click will do it), while the second prop is linking the dropdown visilibity to your custom data boolean value.
As your "data" boolean value is initialized to false (closed dropdown) and is automatically updated to true via the v-model when opening the dropdown, the left thing to do is to pass this value to false on your sortTableData method.
Assuming you're using SFC (but the approach is the same for pure JS components):
Template
<v-menu offset-y :close-on-content-click="false" v-model="isDropdownDisplayed">
...
</v-menu>
Script
{
name: 'MyComponent',
data: function () {
return {
isDropdownDisplayed: false
}
},
methods: {
sortTableData: function (/* args */) {
// ...
this.isDropdownDisplayed = false
// ...
}
}
}

How do I handle the click event of a v-menu in Vuetify?

How can I check and handle the click event of a v-menu in Vuetify?
The best way is to use v-model on v-menu & watch to variable change.
HTML
<v-menu
v-model="isOpened">
</v-menu>
SCRIPT
export default({
watch:{
isOpened: function(){
// do whatever you want
}
}
})
Here is an example:
<template v-slot:activator="{ on: { click } }">
<v-btn #click="(click) (exit=!exit)" icon>
<v-icon v-if="!exit">mdi-dots-vertical</v-icon>
<v-icon v-else>mdi-close</v-icon>
</v-btn>
</template>
As you can see, if the menu is clicked the exit var is set to its opposite value and therefore the mdi-close icon is shown on the button. If the menu is clicked again, the event changing the exit variable will trigger again and the vertical dots will be shown.
I only posted this question to save the exact syntax for myself for later because I have been digging Vuetify docs for hours and finally figured out how to do it.

Using Vuetify tooltip (v-tooltip) component with an external activator (i.e. not wrapped)

I understand how to use Vuetify's v-tooltip with the tooltip wrapping the component. However, I'm not quite sure how to have the activator button outside.
e.g. I have this (non-working code):
<v-tooltip bottom
:activator="$refs.filterBtn"
>
Filter displayed items
</v-tooltip>
<v-btn
ref="filterBtn"
icon
#click="isFilter = !isFilter"
>
<v-icon>fa-filter</v-icon>
</v-btn>
I also tried using prop activator without the v-bind:, same result
Idea: I want the button to be placed separately from tooltip in order to run unit tests. When testing, shallowMount strips anything inside <v-tooltip> so I can't test the button. The problem is I don't know how to make tooltip show up on hover (just like it does when wrapped), I do not want to trigger it with #click.
EDIT: here's codepen
How about using the v-hover UI Component. Wrap it around your button. Bind a boolean variable to the v-hover using v-model, call it buttonHovering. Then bind a boolean variable to the v-tooltip using v-model, call it showToolTip. Then use a watcher to toggle showToolTip true and false based on the value of buttonHovering. Or you can make showToolTip a computed property that always returns the value of buttonHovering. Lastly, bind the disabled attribute of the v-tooltip to the !buttonHovering property to ensure that the tooltip only displays when hovering over the button and not the tooltip's activator.
new Vue({
el: '#app',
data () {
return {
buttonHovering: false,
showToolTip: false
}
},
watch: {
buttonHovering (newVal) {
this.showToolTip = newVal
}
}
})
<link href='https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons' rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.21/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#1.5.16/dist/vuetify.js"></script>
<div id="app">
<v-app>
<v-card>
<v-card-title>
<v-hover v-model="buttonHovering">
<v-btn large>
Hello
</v-btn>
</v-hover>
<v-spacer></v-spacer>
<v-tooltip left v-model="showToolTip" :disabled="!buttonHovering">
<span>Hi from over here!</span>
</v-tooltip>
</v-card-title>
</v-card>
</v-app>
</div>
Try this:
<v-tooltip bottom
v-model="filterBtnTTip"
>
Filter displayed items
</v-tooltip>
<v-btn
icon
#click="isFilter = !isFilter"
#mouseover="filterBtnTTip = true"
#mouseleave="filterBtnTTip = false"
>
<v-icon>fa-filter</v-icon>
</v-btn>
...
data () {
return {
...
filterBtnTTip: false
}
}
<v-app>
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-btn
v-on="on"
>
Your button
</v-btn>
</template>
<span> Your hover message </span>
</v-tooltip>
</v-app>
In my case, it was necessary to place the tooltip together with the button in one component (a custom button with a tooltip function). Therefore, I used a prop activator and added the tooltip itself directly to the button:
<template>
<v-btn
:id="id"
>
<span>
<slot></slot>
<v-tooltip
v-if="tooltipText"
:activator="`#${id}`"
top
>
{{ tooltipText }}
</v-tooltip>
</span>
</v-btn>
</template>
<script>
let id = 0;
export default {
data() {
return {
id: `custom-button-${id++}`, //This is how we get a unique id for each created button
}
}
}
</script>
According to the same scheme, you can place v-tooltip anywhere, the main thing is that at the time of mounting the "activator" already exists.