How to disable carousel in v-tabs - vue.js

I'm doing kind of horizontal navigation bar using v-tabs and v-tabs-items to switch between slides by clicking on the specific tab.
But Vuetify automatically adds carousel to v-tabs component. How can I disable it? I need to make v-tabs component multilined.
I tried to add v-tab inside v-item-group, but it loose all it's styles. Also, hide-slider didn't disable slider too.
<v-tabs v-model="tab" hide-slider>
<v-tab v-for="(item, index) of [1, 2, 3, ..., 100]" :key="index">
{{ item }}
</v-tab>
</v-tabs>
I should render elements like on this picture. If it haven't enought space to for element on current line - it should add it on next.

.v-tabs-bar__content {
flex-wrap: wrap;
width: 100%;
}
div.v-tabs-bar {
height: auto;
}
... will, most likely, solve your problem.
See it working:
Vue.config.productionTip = false;
Vue.config.devtools = false;
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
items: [...Array(100).keys()].map(() => ['anything', 'whatever', 'something'][Math.floor(3 * Math.random())]),
}),
propsData: {
tab: {
type: Number,
default: 0
}
}
})
.v-tabs-bar__content {
flex-wrap: wrap;
width: 100%;
}
div.v-tabs-bar {
height: auto;
}
div.v-tab {
line-height: 3rem; /* or use padding instead */
}
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#3.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.0.15/dist/vuetify.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.0.15/dist/vuetify.min.js"></script>
<div id="app">
<v-tabs>
<v-tab v-for="(item, index) in items" :key="index">
{{item}}
</v-tab>
</v-tabs>
</div>

Related

How to make a proper pagination with Vuejs? Setter not defined error

Working on the pageNav for a personal app I am working on and cannot get the index to show properly.
I figure that making the pageIndexInner and itemsPerPageInner computed propetries was the best route, but when it comes to editing those, I need to also have a setter? I've looked into getters and setters, but am having a very hard time wrapping my head around it.
Without the computer properties, the click event works and I can make it all the way to the itemToal amount, but the index doesn't match up.
If you change the default pageIndex to 3,
I want to see:
but this is what I'm actually seeing:
I'm just not sure where to go with all of this and any guidance would be greatly appreciated. Thank you
Codepen Link:https://codepen.io/LovelyAndy/pen/NWbjLGz?editors=1010
Vue Component code:
<template>
<div class="_table-page-nav-wrapper">
<div #click="back" :disabled="pageIndexInner === 0" class="_arrow-btn">
<
</div>
<div class="_page-index-inner">
{{ itemsTotal }} Total Items {{ pageIndexInnerStart}} - {{ itemsPerPageInnerStart }} Shown
</div>
<div #click="forward" class="_arrow-btn">
>
</div>
</div>
</template>
<style lang="sass" scoped>
._table-page-nav-wrapper
display: flex
justify-content: center
align-items: center
div
display: flex
justify-content: center
align-items: center
._arrow-btn
width: 50px
height: 50px
border-radius: 4px
box-shadow: 0 5px 5px rgba(0,0,0,0.2)
._page-index-inner
width: 244px
height: 50px
border-radius: 4px
box-shadow: 0 5px 5px rgba(0,0,0,0.2)
margin: 0px 20px
</style>
<script>
export default {
name: 'TablePageNavigation',
props: {
/**
* passed values can be either 10 or 25 or 50
*/
itemsPerPage: {
type: Number,
default: 10,
validator: (prop) => [10, 25, 50].includes(prop),
},
pageIndex: {
type: Number,
default: 0,
},
itemsTotal: {
type: Number,
default: 100,
},
},
data() {
return {
pageIndexInner: this.pageIndex,
itemsPerPageInner: this.itemsPerPage,
}
},
computed: {
pageIndexInnerStart() {
return this.pageIndex + this.itemsPerPage
},
itemsPerPageInnerStart() {
return this.itemsPerPage + this.itemsPerPage
},
},
methods: {
back() {
if (this.itemsPerPageInner > this.itemsPerPage) {
this.itemsPerPageInner = this.itemsPerPageInner - this.itemsPerPage
this.pageIndexInner = this.pageIndexInner - this.itemsPerPage
const newIndex = this.pageIndexInner
this.$emit('update:pageIndex', newIndex)
}
return
},
forward() {
if (
this.itemsPerPageInnerStart + this.itemsPerPage > this.itemsTotal ||
this.PageIndexInnerStart + this.itemsPerPage > this.itemsTotal
) {
return
}
this.pageIndexInnerStart = this.pageIndexInnerStart + this.itemsPerPage
this.itemsPerPageInnerStart = this.itemsPerPageInnerStart + this.itemsPerPage
},
},
}
</script>
I commented on your related question earlier this morning, and decided to create an example based on my previous pagination implementation that I mentioned. I removed a lot of your calculations for a simpler approach. I didn't handle all scenarios such as if total items is not a multiple of items per page, but if you like what I did you can work that out on your own. Here is the code from my single file component that I developed in my Vue sandbox app, which uses Bootstrap 4.
<template>
<div class="table-page-navigation">
<button class="btn btn-primary" #click="back" >Back</button>
<span>
{{ itemsTotal }} Total Items {{ pageFirstItem}} - {{ pageLastItem }} Shown
</span>
<button class="btn btn-secondary" #click="forward" >Forward</button>
</div>
</template>
<script>
export default {
name: 'TablePageNavigation',
props: {
/**
* passed values can be either 10 or 25 or 50
*/
itemsPerPage: {
type: Number,
default: 10,
validator: (prop) => [10, 25, 50].includes(prop),
},
itemsTotal: {
type: Number,
default: 100,
},
},
data() {
return {
currentPage: 1,
}
},
computed: {
numPages() {
return this.itemsTotal / this.itemsPerPage;
},
pageFirstItem() {
return (this.currentPage - 1) * this.itemsPerPage + 1;
},
pageLastItem() {
return this.currentPage * this.itemsPerPage;
}
},
methods: {
back() {
if (this.currentPage > 1) {
this.currentPage--;
}
},
forward() {
if (this.currentPage < this.numPages) {
this.currentPage++;
}
},
},
}
</script>
Vuetify
Vuetify pagination Component
This might help if you're comfortable using a UI library.
<!DOCTYPE html>
<html>
<head>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#4.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
</head>
<body>
<div id="app">
<v-app>
<v-main>
<div class="text-center">
<v-pagination
v-model="page"
:length="6"
></v-pagination>
</div>
</v-main>
</v-app>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
<script>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data () {
return {
page: 1,
}
},
})
</script>
</body>
</html>

vuetify v-alert incorrectly dismissing sibling when transition is applied

I am using vuetify v-alert with v-for=alert in alerts looping through alerts array, when an alert is dismissed #input event is fired and I'm removing it from alerts array.
The problem I am facing, that when the element is clicked, transition is applied, and the sibling alert is now shown at the position where the one which was dismissed, seems like the click event is fired for the sibling element as well and the sibling v-alert is isVisible == false but the #input event is not fired.
If I remove transition="scroll-y-reverse-transition" from the v-alert it works properly.
Why is this happening?
const alertsComponent = {
name: "MyAlertsComponent",
template: "#alerts",
data() {
return {
alerts: []
};
},
created() {
this.$root.$off("alert");
this.$root.$on("alert", this.addAlert);
},
methods: {
closeAlert(idx, alert) {
console.log(`deleting alert idx: ${idx} - ${alert}`);
this.$delete(this.alerts, idx);
},
addAlert(alert, ...args) {
alert.type = alert.type || "error";
this.alerts.unshift(alert);
}
}
};
const app = new Vue({
el: "#app",
vuetify: new Vuetify(),
components: {
alertsComponent
},
mounted() {
[...Array(8).keys()].forEach((e) => {
this.fireAlert(this.counter++);
});
},
methods: {
fireAlert(val = this.counter++) {
const alert = this.generateAlert(val);
this.$root.$emit("alert", alert);
},
generateAlert(val, type = "error") {
return {
val,
type
};
}
},
data() {
return {
counter: 1
};
}
});
.alert-section {
max-height: 400px;
padding: 5px;
}
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#4.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
<div id="app">
<v-app>
<v-main>
<v-container>
<header>VueTify!</header>
<hr>
<v-row>
<v-col>
<v-btn #click="fireAlert()">Add Alert</v-btn>
</v-col>
</v-row>
<alerts-component>Hi</alerts-component>
</v-container>
</v-main>
</v-app>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
<template id="alerts">
<div>
<div class="alert-section overflow-y-auto">
<v-alert v-for="(alert, index) in alerts" :key="index" dense dismissible elevation="5" text :type="alert.type" #input="closeAlert(index, alert)" #addAlert="addAlert"
transition="scroll-y-reverse-transition"
>
{{ alert.val }}
</v-alert>
</div>
<hr>
<pre class="code">{{ JSON.stringify(alerts, null, 2) }}
</pre>
</div>
</template>
Looks like the problem is using the index as key.
If I change :key="alert.val" it works fine.

Prevent switch toggle and show alert message v-switch in Vuetify

I'm facing a problem to prevent the toggle for the v-switch and show an alert message. Here is
Codepen example
If I add CSS to prevent the toggle, then click event is not working
.v-input--switch {
pointer-events: none;
}
Also, if I add disabled prop to switch, then click event is not working
new Vue({
el: '#app',
vuetify: new Vuetify(),
data () {
return {
switch1: true,
snackbar: false
}
},
methods:{
showALert(){
this.snackbar = true;
}
}
})
.v-input--switch .v-input--switch__thumb {
color: black !important;
}
.v-input--switch .v-input--switch__thumb.primary--text {
color: black !important;
}
.v-input--switch .v-input--switch__track.primary--text {
color: rgba(0, 0, 0, 0.38) !important;
}
.v-input--switch.v-input--is-disabled:not(.v-input--is-dirty) .v-input--switch__thumb {
color: black !important;
}
.v-input--switch .v-input--switch__thumb:after {
content: "£";
color: #fff;
}
//.v-input--switch {
// pointer-events: none;
//}
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#5.x/css/materialdesignicons.min.css" rel="stylesheet"/>
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.3.14/dist/vuetify.min.css" rel="stylesheet"/>
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.3.14/dist/vuetify.min.js"></script>
<div id="app">
<v-app id="inspire">
<v-container class="px-0" fluid>
<v-switch :ripple="false" flat #change="showALert"></v-switch>
<v-snackbar v-model="snackbar" bottom right>
This has been set to Historical Mode
<template v-slot:action="{ attrs }">
<v-icon v-bind="attrs" color="white" small #click="snackbar = false">
mdi-close
</v-icon>
</template>
</v-snackbar>
</v-container>
</v-app>
</div>
Can someone help me with this? Any solutions would be appreciated.
You can use the readonly prop of the v-switch which prevents toggling of the switch when clicked. You can also add #click directly on v-switch to handle whatever behavior you want to happen when the switch is clicked like showing an alert or validating a value before toggling the switch programmatically.
<v-switch #click="showALert" readonly></v-switch>

Vue List Move Transitions Only Work When Reordering, But Not Splicing

I have a Vue.js application (tested with both Vue 2 and 3) which displays a list of items in a transition-group which has a fade effect and should also be applying a list move transition. Anytime items are reordered (i.e an item that was prevously after a another item is moved before it), the move transition is applied, but if an item is removed with splice and the others stay in the same order, the move transition doesn't apply and the items after the removed one snap into place immediately. Here's a minimal example:
new Vue({
el: '#app',
data: () => ({
list: [1, 2, 3]
}),
methods: {
reorder() {
this.list.unshift(this.list[this.list.length - 1])
this.list.splice(this.list.length - 1, 1)
},
remove() {
this.list.splice(1, 1)
}
}
})
button {
display: block;
}
li {
transition: 0.2s;
}
.v-enter-active, .v-leave-active {
transition: opacity .5s;
}
.v-enter, .v-leave-to {
opacity: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="reorder">Reorder</button>
<button #click="remove" :disabled="list.length < 3">Remove Middle Item</button>
<transition-group tag="ul">
<li v-for="item in list" :key="item">{{ item }}</li>
</transition-group>
</div>
This is my first answer in stack overflow, sorry in advanced if I'm not that accurate with my answer.
From the vue.js documentation I managed to make it work by adding position absolute to the class .v-leave-active. I also modified the li transition duration to match with the opacity transition timing.
I hope it helped!
new Vue({
el: '#app',
data: () => ({
list: [1, 2, 3]
}),
methods: {
reorder() {
this.list.unshift(this.list[this.list.length - 1])
this.list.splice(this.list.length - 1, 1)
},
remove() {
this.list.splice(1, 1)
}
}
})
button {
display: block;
}
li {
transition: 0.5s;
}
.v-enter-active, .v-leave-active {
transition: opacity .5s;
}
.v-leave-active {
position: absolute;
}
.v-enter, .v-leave-to {
opacity: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="reorder">Reorder</button>
<button #click="remove" :disabled="list.length < 3">Remove Middle Item</button>
<transition-group tag="ul">
<li v-for="item in list" :key="item">{{ item }}</li>
</transition-group>
</div>

Hide div when clicking the main div, but not when clicking child divs, hrefs, spans etc

So, when you click the blue content, I want to hide it, but not if you click a child element, like a h2, a, p, etc. Is this possible?
Please see the fiddle
html
<html>
<head></head>
<body>
<div id="app">
<div v-if="modal" id="content" #click="toggle">
<h3>
Name
</h3>
<span>
Like this (don't close if clicked)
</span>
</div>
<button #click="toggle">Toggle</button>
</div>
</body>
</html>
.js
new Vue({
el: '#app',
data: {
modal: true
},
methods: {
toggle: function( ){
this.modal = !this.modal
}
}
});
.css
a {
color: white
}
#content {
width: 300px;
height: 300px;
background: blue;
color: white;
}
Fiddle: http://jsfiddle.net/awu752mr/6/
I believe that is what the v-on:click.self modifier does. See here in the docs.
As #LShapz answered, the modifier=.self should be the recommended approach (Vue Style).
Another way is compare $event.target === $event.currentTarget (not recommended for your use case, but you may use these two properties for some other situations in future).
Vue.config.productionTip = false
new Vue({
el: '#app',
data: {
modal: true
},
methods: {
toggle: function(ev){
if(ev.target === ev.currentTarget) this.modal = !this.modal
}
}
});
a {
color: white
}
#content {
width: 300px;
height: 300px;
background: blue;
color: white;
}
<script src="https://unpkg.com/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<div v-if="modal" id="content" #click="toggle($event)">
<h3>
Name
</h3>
<span>
Like this
</span>
</div>
<button #click="toggle">Toggle</button>
</div>