VUE - vue draggable wont drag back into empty list column - vue.js

I'm currently developing a Project Management application using the vue-draggable feature, which allows me to drag tasks into three distinct phases: ToDo, Progress, and Completed. The feature partially works; I can drag all tasks to different phases, but I cannot drag back into an empty phase. Here's the codesandbox and picture. Am I missing something?
<template>
<div>
<v-row>
<v-col>
<draggable :list="todos" group="people" #change="log"></draggable>
</v-col>
<v-col>
<draggable :list="progress" group="people" #change="log"></draggable>
</v-col>
<v-col>
<draggable :list="completed" group="people" #change="log"></draggable>
</v-col>
</v-row>
</div>
</template>
<script>
import draggable from "vuedraggable";
export default {
name: "two-lists",
display: "Two Lists",
order: 1,
components: { draggable },
data() {
return {
todos: [],
progress: [],
completed: [],
};
},
};
</script>

That's becuase you haven't set any min-height to your draggable. So when they are empty the active area of the draggable shrinks all the way down. You can still move the cards if you drag them to the "To Do" title for example, but for a better UX you should set at least a minimum height to it or make them use all the available height so it's easier for the user to drag them back.
<draggable
class="list-group"
:list="todos"
group="people"
#change="log"
style="min-height:400px"
>
...
</draggable>

Related

Is it possible to create a grid layout in a list?

disclaimer: I'm just learning vue.js and vuetify and I'm not very familiar with javascript. I'm writing my first vue.js app.
I'm using vue 2.x and vuetify 2.x.
I need to create a scrolable list of items on which the user will be able to perform actions. For now, I'm just concerned about the layout of the list items.
The item is made of two visible information, a number and a text (like an inventory) that should be displayed in two columns. The numbers should be right aligned in their column and the text left aligned.
This is what I have so far.
<template>
<v-list dense>
<template v-for="(item, index) in items">
<v-list-item :key="item.r">
<v-list-item-content class="font-weight-bold">
<v-list-item-title>
{{ item.n }} {{ item.t }}
</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-divider :key="index"></v-divider>
</template>
</v-list>
</template>
<script>
export default {
name: "itemList",
data() {
return {
items: this.$store.state.currentItems,
};
},
};
</script>
Is it possible to use a layout inside a list item ? If not, how could I create the desired column alignment ?
In case it could influence the implementation, the actions are
make the item a sliding button with multiple selectable actions
or make the number and the text individual clickable flat buttons with popup menus
manually reorder list items with drag and drop.
An alternative to the sliding button (if not possible) is to make the number and text flat buttons with a popup menu. I guess this could influence how to define the layout.
generally with the use of v-row and v-col you can implement a layout and this is true inside of a v-list-item-content, for example check the code below (I'm not sure if I understood the layout you've asked for correctly or not but it must give you a hint)
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: {
items: [{
t: 'title one',
n: 1
},
{
t: 'title two',
n: 2
},
{
t: 'title three',
n: 3
},
{
t: 'title four',
n: 4
},
],
}
})
<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>
<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>
<v-list dense>
<template v-for="(item, index) in items">
<v-list-item :key="item.n">
<v-list-item-content class="font-weight-bold">
<v-row>
<v-col cols="6">
<v-row no-gutters justify="end">{{ item.n }}</v-row>
</v-col>
<v-col cols="6">
<v-row no-gutters>{{ item.t }}</v-row>
</v-col>
</v-row>
</v-list-item-content>
</v-list-item>
<v-divider :key="`divider-${item.n}`"></v-divider>
</template>
</v-list>
</v-container>
</v-main>
</v-app>
</div>

Vuetify change checkbox icon in v-select / v-combobox

I use Vuetify but disabled the import of all icons since treeshaking wasn't working properly in Nuxt, instead I followed the advice and import them manually as stated in this thread: vuetifyjs: Adding only used icons to build
However, this means that a lot of components that require icons, e.g v-checkbox, v-select or v-combobox (which uses v-checkbox in their dropdown menus) need their icons added manually. Just using v-checkbox allows for :on-icon & :off-icon props to be used but I can't figure out how I'd reach them when the checkboxes are used by other components.
I've been attempting to change the behaviour in both v-select and v-combobox.
This is as far as I got but clearly this doesn't add the checked icon, just the blank one.
<v-combobox outlined multiple chips v-model="select" :items="items">
<template v-slot:item="{ item }">
<v-icon>{{mdiCheckboxBlankOutline}}</v-icon>{{ item }}
/template>
</v-combobox>
import { mdiCheckboxBlankOutline, mdiCheckboxMarked } from "#mdi/js";
Data(){
select: ["Stockholm"],
items: [
"Stockholm",
"London",
],
}
My question is therefore, how can replicate the default checkbox behaviour for the combobox menu using imported icons?
This thread seems to talk about it but never ends up showing a code example:
https://github.com/vuetifyjs/vuetify/issues/10904
(Meaning it should look like this)
You can use the item slot, where you are provided with the item, on and attrs object, to replicate the default behaviour.
You bind the on (events) and attrs (properties) objects to the custom list item, to send click events from your list item to combobox component.
Next you set the appropriate icon depending on the selected values. See the code below and the code sandbox.
<template>
<v-app>
<v-combobox
label="Label"
outlined
multiple
chips
v-model="select"
:items="items"
>
<template v-slot:item="{ item, on, attrs }">
<v-list-item v-on="on" v-bind="attrs">
<v-list-item-icon>
<v-icon>
{{ select.includes(item) ? checkIcon : uncheckIcon }}
</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title v-text="item" class="text-left"></v-list-item-title>
</v-list-item-content>
</v-list-item>
</template>
</v-combobox>
</v-app>
</template>
<script>
import { mdiCheckboxBlankOutline, mdiCheckboxMarked } from "#mdi/js";
export default {
name: "HelloWorld",
data() {
return {
items: ["One", "Two", "Three"],
select: [],
uncheckIcon: mdiCheckboxBlankOutline,
checkIcon: mdiCheckboxMarked,
};
},
};
</script>
CodeSandbox: https://codesandbox.io/s/recursing-banach-cb7ys?file=/src/components/HelloWorld.vue

Weird Vue Render issue on embedded list that uses Vuetify Badges

I have a list of users that I'm trying to add an icon to enable/disable their account. So I render the list of users in a v-for loop. And I track the visibility of a badge on each icon with an array indexed by the loop index.
I'm having a weird issue where Vue doesn't recognize the change to the array value unless I update some other dummy variable. Is this a Vuetify issue. Or just a Vue Reactivity issue?
I created a Codepen that show my issue.
https://codepen.io/DedicatedManager/pen/eYmZRQo?editors=1010
<div id="app">
<v-app id="inspire">
<v-container fluid class="text-center">
The two lists are the same differing only by changing a dummy variable in the "mouseover" and "mouseout" function that causes the badge to display/hide. But the one on the left doesn't show the badge on hover. The one on the right has the extra dummy variable that somehow forces the rendering to work and thus the badge shows (it shows on both sides because they use the same variable to hold the boolean for the v-model.
<v-row
justify="space-between"
>
<v-col cols="6" class="mt-12">
<div v-for="(listItem,index) in myData" :key="index">
<v-badge v-model="showCircle1[index]" overlap>
<template v-slot:badge>
<span><v-icon>mdi-delete</v-icon></span>
</template>
<v-icon large color="grey" #mouseover="showCircle1[index]=true;" #mouseout="showCircle1[index]=false;">mdi-email</v-icon>
</v-badge>
</div>
</v-col>
<v-col cols="6" class="mt-12">
<div v-for="(listItem,index) in myData" :key="index">
<v-badge v-model="showCircle1[index]" overlap>
<template v-slot:badge>
<span><v-icon>mdi-delete</v-icon></span>
</template>
<v-icon large color="grey" #mouseover="showCircle1[index]=true; mouseOverVal=true" #mouseout="showCircle1[index]=false; mouseOverVal=false">mdi-email</v-icon>
</v-badge>
</div>
</v-col>
</v-row>
mouseOverVal: {{mouseOverVal}}<br>
showCircle1: {{showCircle1}}<br>
</v-container>
</v-app>
</div>
Javascript
new Vue({
el: '#app',
vuetify: new Vuetify(),
data () {
return {
showCircle1:[false,false,false,false],
myData:['one','two','three','four'],
mouseOverVal:false,
}
},
})
Vue cannot detect changes when you set the value of an item in an array.
From https://v2.vuejs.org/v2/guide/list.html#Caveats:
For example:
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x' // is NOT reactive
vm.items.length = 2 // is NOT reactive
[...] both of the following will accomplish the same
as vm.items[indexOfItem] = newValue, but will also trigger state
updates in the reactivity system:
// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)
See the link for further details and additional related approaches.

how can i call modal component inside a v-for loop?

I use vuetify https://vuetifyjs.com/en/
My parent component like this :
<template>
<v-container>
...
<v-col
v-for="(item, i) in items"
:key="i"
>
<v-card
>
<v-app-bar dark color="grey">
<v-toolbar-title>Weekly Schedule : {{item.name}}</v-toolbar-title>
<div class="flex-grow-1"></div>
<modal-datepicker :schedule="item" />
</v-app-bar>
</v-col>
...
</v-container>
</template>
<script>
import { mapState } from "vuex";
import modalDatepicker from "../views/modalDatepicker";
export default {
components: {modalDatepicker},
};
</script>
My child component like this :
<template>
<v-dialog
:ref="dialog"
v-model="modal"
:return-value.sync="date"
>
<template v-slot:activator="{ on }">
<v-btn color="success" dark v-on="on">Show datepicker</v-btn>
</template>
<v-row justify="center">
<v-date-picker v-model="date" scrollable>
<div class="flex-grow-1"></div>
<v-btn text color="primary" #click="modal = false">Cancel</v-btn>
<v-btn text color="primary">OK</v-btn>
</v-date-picker>
</v-row>
</v-dialog>
</template>
<script>
import { mapState } from "vuex";
import modalDatepicker from "../views/modalDatepicker";
export default {
components: {modalDatepicker},
props: ['schedule'],
data: () => ({
date: new Date().toISOString().substr(0, 10),
modal: false,
}),
mounted() {
console.log(this.schedule)
},
};
</script>
The code above works, but seems inefficient. because every time the loop, it call modal dialog. I want modal to only be called when user click show datepicker button
how do i do that?
Apologies as I am on my mobile. Have you considered having the modal in your parent component outside of the loop?
You could have a data variable to handle your modal e.g
modal: {
open: false,
schedule: null }
Essentially then when your button is clicked you could add a v-click with a function that controls this data which in turns handles the content in the modal.
Then on your date picker or modal have a function to handle the update/close to clear this data and handle whatever you need too outside.
Then you could combine this into one component.
I see you have vuex you could also use vuex to control your modal and it’s contents.

Center content in Vuetify project / Grid system

I am starting learning Vue.js and I got a problem with understanding Vuetify's grid system. Now I want to center that content to the center. I've read docs and tried for many ways, but positioning elements it is still random, I would say.
Can anybody explain me v-content, v-layout and v-flex?
Here's code.
<template>
<v-container>
<v-layout text-center>
<v-flex xs4 >
<v-img
:src="require('../assets/logo.svg')"
class="my-3"
contain
height="200"
>
</v-img>
<v-text-field v-model="newPrice" placeholder="Add price"></v-text-field>
<v-btn #click="addPrices">Add price </v-btn>
<v-text-field v-model="newVolume" placeholder="Add volume"></v-text-field>
<v-btn #click="AddVolume">Add volume </v-btn>
</v-flex>
</v-layout>
</v-container>
</template>
<script>
export default {
name: 'MainPage',
data () {
return {
prices: [],
volumes: [],
newPrice: '',
newVolume: ''
}
},
methods: {
addPrices () {
this.prices.push(this.newPrice)
},
addVolume () {
this.volumes.push(this.newVolume)
}
}
}
</script>
So vuetify's grid system takes advantage of CSS flexbox. v-container determines the spacing around and between elements; v-layout determines the flex axis/direction/order of the elements, and v-flex determines how big the elements will be in the 12-point grid system.
To center something, try the below:
<v-layout row justify-center></v-layout>
This is equivalent to:
.layout {
display: flex;
flex-direction: row;
justify-content: center;
}
There is no prop in the v-layout component named text-center so I'm guessing that's why it's not centering.