set image height based on breakpoint - vue.js

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

Related

Vue Element UI dynamically changes el-select color

I tried to modify el-select to match the color of el-option.
The methods I search on the Internet always require me to modify the style scoped . It is a static method and cannot be changed dynamically.
My expectation is when I select "WIP" in the options, it will become a yellow label in el-select according to the color in the options.
The following is a rough demo I created in Codesandbox.
https://codesandbox.io/s/dynamically-change-el-select-color-based-on-status-v0u8d?file=/src/views/Editor.vue
Your kind assistance will be greatly appreciated, thank you very much.
You could set the class of the el-select based on that you could color the input value based on the selected value
<el-select
v-model="scope.row.status"
:class="getStatusColorClass(scope.row.status)"
placeholder="Select"
>
<el-option
v-for="(status, index) in statusList"
:key="index"
:label="status.name"
:value="status.id"
>
<span :style="{ color: status.color }">{{ status.name }}</span>
</el-option>
</el-select>
JS:
methods: {
getStatusColorClass(id) {
if (!id) {
return {};
}
return this.statusList.find(status => status.id === id).name;
}
}
SCSS:
<style lang="scss">
.Approved input {
color: #00A86B;
}
.Retake input {
color: #ED2939;
}
.WIP input {
color: #FCE205;
}
</style>
After a ton of research, finally I found the solution.
The following is a rough demo I created in Codesandbox.
https://codesandbox.io/s/dynamically-change-el-select-color-based-on-status-v2-od8gn?file=/src/views/Editor.vue
I'm not sure if this is the best method, but this method has solved my problem. If you have a better solution, welcome to provide here.
Thank you very much!

VueJS - Is there a way to conditionally apply a transition?

What I would like to do is create an alert box component with a custom reveal transition that is optional, something like this (edited for brevity):
<template>
<transition v-if="withTransition">
<b-alert v-bind="this.$attrs" />
</transition>
</template>
I will have a withTransition prop that ideally will conditionally render only the transition effect, and not affect the rendering of the alert box.
v-if and v-show won't work because those properties would show and hide the alert component as well. Plus, AFAIK <transition> does not render as a DOM element, so I'm not sure how that could be conditionally rendered, if at all.
Any suggestions?
You can use dynamic transitions and bind to a transition name (using a computed property) that does nothing when you want the effect disabled.
For example...
new Vue({
el: '#app',
data: () => ({ withTransition: true, show: false }),
computed: {
computedTransition () {
return this.withTransition && 'fade'
}
}
})
.alert {
background-color: #f99;
border: 1px solid #f66;
padding: 1rem;
}
.fade-enter-active, .fade-leave-active {
transition: opacity .5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<div id="app">
<p>
<label>
With transition:
<input type="checkbox" v-model="withTransition"/>
</label>
</p>
<p><button type="button" #click="show = !show">Toggle alert</button></p>
<transition :name="computedTransition">
<p class="alert" v-if="show">Something happened</p>
</transition>
</div>
Use an inline ternary condition
If you just need to quickly check against a variable, this is an option:
<transition :name="someVariable == 'someValue' ? 'myTransitionA' : 'myTransitionB'">
someVariable could be anything (prop, data, computed, method...).
Set a transition name (e.g.'myTransitionB') to just an empty string '' if you wanted no transition at all for a particular condition.
I use this technique to toggle transitions based on different UI contexts (e.g. mobile vs desktop). For example, I want control bars to slide in-and-out in mobile, but I want them to be static on desktop.

Vuetify how to mark field as required

When we try to fill forms in the internet, required fields are marked using a red color ' * ' mark to indicate that the field is a must.
Like that is there a way to indicate users to required fields in vuetify.js?
It's a bit of a pain, but there is a 'label' named slot and you can do something like this:
<v-text-field
v-model="loginInfo.email"
autofocus
name="email"
type="email">
<template #label>
<span class="red--text"><strong>* </strong></span>Email
</template>
</v-text-field>
From v1.1.0 docs:
The required prop no longer explicitly adds an asterisk to the label. All of its functionality to validation was removed for v1.0.
So apparently nothing will set it as required anymore, we have to add it manually in the label:
label="Name*"
Or you could use CSS:
.required label::after {
content: "*";
}
Tho you must add required class manually (name of the class is arbitrary of course).
You are able to pass a rules prop to a v-text-field.
E.g,
<v-text-field
v-model="title"
:rules="['Required']"
label="Title"
counter
maxlength="20"
></v-text-field>
See this Vuetify example for a fuller picture: https://github.com/vuetifyjs/vuetifyjs.com/blob/master/src/examples/text-fields/validation.vue
required is also an HTML property. You could just add it to the HTML Element like this:
<v-text-field
v-model="title"
label="Title"
counter
maxlength="20"
required
></v-text-field>
I have the solution, with add this css class into main css file or in main.js.
div[aria-required=true].v-input .v-label::after {
content: " *";
color: red;
}
That's it, in this add field required:
<v-text-field required>
Performance wise, I don't know if this is the best solution. But it works.
Import the JavaScript file bellow into you application bootstrap (or something like that).
import Vue from 'vue';
Vue.mixin({
mounted() {
const e = this.$el;
if ('querySelector' in this.$el) {
const i = this.$el.querySelector('input[required]');
if (i !== null) {
const l = i.previousSibling;
if (l.querySelector('.required.sign') === null) {
const r = document.createElement('span');
// l.classList.add('required');
r.classList.add('required', 'sign');
r.appendChild(document.createTextNode('*'));
l.appendChild(r);
}
}
}
},
});
Nuxt.js: put the file above into the plugins folder. Include its path on the plugins array on the nuxt.config.js file.
Add the rule bellow to your global CSS / theme.
.v-label > .required.sign {
color: darkred;
font-weight: bold;
margin-left: .25em;
}

Changing the Vuetify carousel height

Is it possible to reduce the height of a <v-carousel>? I saw in the documentation that they were working on it, but has it been implemented yet? If not is there a work around? I'm just trying to set a max height.
You can set auto height of it:
<v-carousel height="auto">
There's no need to use CSS neither important on it.
You can use one prop to change the height:
Name: height
Default: 500
Type: number | string
Sets the component height
Eg.
<v-carousel height="500px">
<v-carousel-item
v-for="(item,i) in items"
:key="i"
:src="item.src"
></v-carousel-item>
</v-carousel>
In your main vue component or in the component where you have the v-carousel add a css rule:
.carousel {
height: 200px !important;
}
Add !important if it's not working without it
If you're putting this rule in the main component make sure the <style> tag doesn't have the word scoped in it
If you want dynamic height of v-carousel based on image, for example.
You can use something like this:
example:
<v-carousel :style="{'height': this.carouselHeight}">
<v-carousel-item v-for="(item, index) in items" :key="index" style="height: auto" :src="item.image"></v-carousel-item>
</v-carousel>
set data prop:
data () {
return {
carouselHeight: 0
{
}
create method:
getCarouselHeight () {
var item = document.getElementsByClassName('v-image__image--cover')
this.carouselHeight = item[0].clientHeight + 'px'
}
call method on mounted:
mounted () {
this.getCarouselHeight()
}

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).