Why is my vuetify dialog hidden behind this evil overlay? - vue.js

I have a vuetiful component which should just display a dialog. Unfortunately, an evil overlay has taken over the domverse. How do I overcome the forces of semi-transparent darkness?
Vue.component('step-form', {
data: function() {
return {
dialog: false
}
},
methods: {
showDialog() {
this.dialog=!this.dialog;
}
},
template: `
<v-dialog v-model="dialog" persistent max-width="600px">
Help, I'm hidden behind this evil "overlay"!
</v-dialog>
`
});
https://codepen.io/anon/pen/jJpWGx

It's not.
You simply don't have background color inside v-dialog. You can put v-card inside for example.
You just used persistent property which makes you unable to close it on-overlay-click, and have no other means to close it.
So dialog has z-index: 202, and overlay has 201 apparently, so dialog is above overlay,
but box-shadow makes it look like like it's floating behind it or something, but it's because it's transparent, and you just need to set background-color.

use hide-overlay
change to below code
<v-dialog hide-overlay
v-model="dialog" persistent max-width="600px">
Help, I'm hidden behind this evil "overlay"!
</v-dialog>
Documentation : https://vuetifyjs.com/en/components/dialogs

Related

set image height based on breakpoint

Vuetify's image component ( https://vuetifyjs.com/en/components/images/ ) provides a height prop. How can I keep that height variable based on the current breakpoint? It would be nice to have something like this
<v-img
:height="img.height.xs"
:sm="img.height.sm"
:md="img.height.md"
:lg="img.height.lg"
:xl="img.height.xl">
</v-img>
Do I have to do it with a calulated property or is there a way to solve it with HTML only? By that, I mean I'm looking for a solution like the TailwindCSS approach ( https://tailwindcss.com/docs/height/#responsive )
<div class="h-8 sm:h-12 md:h-16 lg:h-20 xl:h-24"></div>
I created a code snippet for example purposes
https://codepen.io/dsm98861/pen/qBbXomN?editable=true&editors=101%3Dhttps%3A%2F%2Fvuetifyjs.com%2Fen%2Fcomponents%2Fcards%2F
Vuetify has its own predefined and overridable breakpoints.
I think, the most correct way to solve your problem is to use computed props. According to your codepen, it should be something like this:
<div id="app">
<v-app id="inspire">
<v-card
class="mx-auto"
max-width="400"
>
<v-img
class="white--text align-end"
src="https://cdn.vuetifyjs.com/images/cards/docks.jpg"
:height="imageHeight"
></v-img>
</v-card>
</v-app>
</div>
new Vue({
el: '#app',
vuetify: new Vuetify(),
computed: {
imageHeight () {
switch (this.$vuetify.breakpoint.name) {
case 'xs': return '200px'
case 'sm': return '400px'
case 'md': return '600px'
case 'lg': return '800px'
case 'xl': return '1000px'
}
},
}
})
If you are really want to solve it HTML-only, you could set height prop this way:
<v-img
class="white--text align-end"
src="https://cdn.vuetifyjs.com/images/cards/docks.jpg"
:height="$vuetify.breakpoint.xs
? '200px'
: ($vuetify.breakpoint.sm
? '400px'
: ($vuetify.breakpoint.md
? '600px'
: ($vuetify.breakpoint.lg
? '800px'
: '1000px'
)
)
)"
></v-img>
You may be able to come up with a more elegant and suitable solution after reading the article about breakpoints in Vuetify docs.
Why do you want to use HTML only?
Don't forget that use can change window size and you need to react on it dynamically. Which is not possible with plain HTML. This is possible with script but it's complicated. Using media queries is easy and proper tool.
I don't know what CSS pre-processor you are using. But for SASS you can use conditional styles based on breakpoint (compiled to media queries)
#media #{map-get($display-breakpoints, 'md-only')}
img
width: 200px
#media #{map-get($display-breakpoints, 'sm-and-down')}
img
width: 100px

this.$vuetify.goTo(0) not working inside v-navigation-drawer in vuetify

This my code:
<template>
....
<v-navigation-drawer app absolute width="340" permanent>
....
<v-btn fab dark fixed bottom left color="primary" #click="$vuetify.goTo(0)">
<v-icon>keyboard_arrow_up</v-icon>
</v-btn>
...
<v-navigation-drawer>
<template>
When I click the button as shown in the code, it should scroll to the top of the drawer. But it doesn't. Can anyone help me?
You need to apply container, which is one of the options for scroll. If you are not providing this, vuetify won't know to place it on your nav drawer.
Add a ref to your nav drawer:
<v-navigation-drawer app absolute width="340" permanent ref="myNavDrawer">
You can assign the element in your script, so that we can refer to it in goTo:
mounted() {
this.navDrawerContent =
this.$refs['myNavDrawer'].$el.querySelector('div.v-navigation-drawer__content');
}
Then for your button, pass 0 as you did, but add the container:
<v-btn ... #click="$vuetify.goTo(0, { container: navDrawerContent } )">
A CODEPEN for your reference.

How to disable transition-group only on page load?

I have a transition-group that renders a div, and within that is a component with a v-for attribute that renders several items. I then have a button that adds a new item to the beginning of the array. That transition works perfectly.
The only thing I don't like, is that the entire list loads with the transition on page load, and I'd like to disable it only on page load. I've searched Stack and Google but couldn't find a way. Is there a way to do this, so that transitions still works on button click, but is disabled for page load?
<transition-group
name="item-list"
tag="div">
<item-row
v-for="item in items"
:key="item.id"
:item="item" />
</transition-group>
.item-list-enter-active,
.item-list-leave-active,
.item-list-move {
transition : 250ms cubic-bezier(0.59, 0.12, 0.34, 0.95);
transition-property: opacity, transform;
}
.item-list-enter {
opacity : 0;
transform: translateX(50px) scaleY(0.5);
}
.item-list-enter-to {
opacity : 1;
transform: translateX(0) scaleY(1);
}
.item-list-leave-active {
position: absolute;
}
.item-list-leave-to {
opacity : 0;
transform : scaleY(0);
transform-origin: center top;
}
I wish I could've found a more "Vue-y" way of handling this, however I ended up going this route. Essentially I added a class to the body and removed all transitions. Then on the created lifecycle of my component, I remove that class. This removes the transition on page load, but still keeps the transition on click of the button like I want.
You can dynamically change the name value of the transition-group. Maybe on page load have a value different from the value that has the correct class name that the CSS targets. Then in the mounted lifecycle hook you can change it back to the correct class name.
You need to bind the duration for transition-group
template:
<transition-group
:duration="duration"
name="item-list"
tag="div">
<item-row
v-for="item in items"
:key="item.id"
:item="item" />
</transition-group>
script:
data() {
return {
duration: 0,
items: [
{id: 1},
{id: 2}
]
}
},
methods: {
add() {
if(this.duration===0) this.duration = 250
this.items.push({id: 'xxx'})
}
}
In case anyone comes across this like I did.
I ended up achieving this by having a transitionsOn flag added to the data (didn't seem to matter what it was initialised to), and a computed name for the transition, i.e.
<transition-group :name="transitionName">
in computed, I then had, for a transition called 'flash'
computed: {
transitionName() {
return this.transitionsOn ? 'flash' : 'disabled';
},
},
I would then set this.transitionsOn = true when I wanted it to fire.
Took a lot of fiddling about to figure this out but it seems to work OK

Quill.js doesn't work properly in a Vuetify v-dialog

I'm trying to use a Quill.js editor inside a Vuetify v-dialog, but the toolbar dropdowns are not closed when the user clicks outside the current opened dropdown.
I made a js Fiddle:
https://jsfiddle.net/6d7bef5n/
<div id="app">
<v-app>
<quill-editor v-model="content"></quill-editor>
<v-dialog v-model="dialog">
<quill-editor v-model="contentKo"></quill-editor>
</v-dialog>
<v-btn #click.stop="dialog = !dialog">Open Quill in a Modal</v-btn>
</v-app>
</div>
Vue.use(VueQuillEditor)
Vue.use(VueQuillEditor)
new Vue({
el: "#app",
data() {
return {
content: "I'm OK",
contentKo: "I'm Wrong, Toolbar dropdowns are not closing on blur",
dialog: false
}
}
});
It seems that the v-dialog component does something wrong on the events inside his content slot, probably for the open/close behavior, but didn't found what.
Thanks
As #MarlburroW pointed out Vuetify's VDialog components stops the propagation of the click event when the user clicks inside of the dialog.
https://github.com/vuetifyjs/vuetify/blob/master/packages/vuetify/src/components/VDialog/VDialog.js#L284
In my case I had a custom directive which detects clicks outside of the target element, for example for a dropdown component. This worked, but if you used such a component inside of Vuetify's dialog the custom directive would not work, because the VDialog stopped propagation of the click event.
Vuetify has its own outside click directive which they use for menus, selects...etc. It does not suffer from this issue.
https://github.com/vuetifyjs/vuetify/blob/master/packages/vuetify/src/directives/click-outside.ts
I had a look at the differences between Vuetify's directive and my own and the reason it works is that they use capturing instead of bubbling for the event listener.
The following codepen demonstrates it:
https://codepen.io/geersch/pen/LoLgYK
onClick = function (e) { console.log('The click event bubbled up.'); };
document.body.addEventListener('click', onClick, { capture: true });
// document.body.addEventListener('click', onClick, { capture: false });
dialog = document.querySelector('#dialog');
dialog.addEventListener('click', function (e) {
e.stopPropagation();
});
So I just changed my directive to use capturing too.
.quill-editor {
user-select: auto !important;
-moz-user-select: auto !important;
-webkit-user-select: auto !important;
-ms-user-select: auto !important;
}
Try it. It works for me.

vuetify: calculate grid width by passed props

I want to make a menu component that builds same-sized buttons based on the amount of menuItems passed in as props.
similar to
https://stackoverflow.com/a/47215040/6066886
i want to give the v-flex a conditional attribute. (xs6 for two items, xs4 for three and so on)
(something like <v-flex v-for="item in menuItems" xs[12 / menuItems.length]>)
in the linked question, the idea is to pass "xs10" or not, based on a condition.
i want to calculate WHICH of those will be added to my v-flex, but i have no idea how that would be done.. as i can't v-bind anything... can anyone help me?
You can bind a computed property as class object:
<v-flex :class="xsComputed" v-for="(item,i) in items" :key="i">{{item}}</v-flex>
...
new Vue({
el: '#app',
data: function() {
return {
items: [1, 2, 3, 4]
}
},
computed: {
xsComputed: function() {
var step = Math.floor(10 / this.items.length)
var xsc = {}
xsc['xs' + step] = true
console.log(xsc)
return xsc
}
}
})
https://jsfiddle.net/26zfLn8j/
Ok, I see your problem now. I was digging around a little and it's not easy to solve, cause the v-flex doesn't support params, it should be declared as a attribute and not class (as says this response https://stackoverflow.com/a/47215040/6058255).
As there as no way to create a attribute without value, like I said in the comment, and affter some tests, I will do as follow:
<v-flex v-bind:class="'xs'+12/menuItems.length" v-for="menuItem in menuItems">
{{menuItem}}
</v-flex>
This should render the v-flex element with a class like: "xs12","xs6","xs4"..
The then you should only copy the class style for this elements that have the v-flex for it, something like:
.xs12{ max-width:100%; }
.xs6{ max-width: 50%; }
.xs4{ max-width: 25%; }
.xs3{ max-width: 33.333333333%; }
...
Maybe is not the more elegant solution but it's easy and it works for you I think.
Hope it helps!
EDIT:
Reviewing stdob-- answer I now see that only with the expression:
<v-flex v-bind:class="'xs'+12/menuItems.length" v-for="menuItem in menuItems">
{{menuItem}}
</v-flex>
Should work. The flex component will render as flex xs12 (for instance).