v-spacer in datatable grouping header - css

I have a Vuetify project. I'm using the template with a slot group.header, so far so good.
Now I want to use a <v-spacer> in this template. Its in a <v-sheet>.
When I use the <v-spacer></v-spacer> I expect the content on the same line. But it will be on the next line. How can I display the button after the spacing on the same line, user Vuetify's system?
codepen
https://codepen.io/h3ll/pen/VwWOxdJ?editors=1010
HTML
<v-data-table
:headers="headers"
:items="desserts"
item-key="name"
sort-by="name"
group-by="category"
class="elevation-1"
show-group-by
>
<template v-slot:group.header="{items, isOpen, toggle}">
<th #click="toggle" colspan="12" class="ma-0 pa-0">
<v-sheet>
<v-icon class="mr-3">{{ isOpen ? 'mdi-folder-open' : 'mdi-folder' }}</v-icon>
{{ items[0].category }}
<v-spacer></v-spacer>
<v-btn x-small>I WANT TO BE ON THE RIGHT SIDE</v-btn>
</v-sheet>
</th>
</template>
</v-data-table>
javascript
new Vue({
el: '#app',
vuetify: new Vuetify(),
data () {
return {
headers: [
{
text: 'Dessert (100g serving)',
align: 'start',
value: 'name',
groupable: false,
},
{ text: 'Category', value: 'category', align: 'right' },
{ text: 'Dairy', value: 'dairy', align: 'right' },
],
desserts: [
{
name: 'Frozen Yogurt',
category: 'Ice cream',
dairy: 'Yes',
},
{
name: 'Ice cream sandwich',
category: 'Ice cream',
dairy: 'Yes',
},
...
],
}
},
})
Update
I can fix it with normal css like
<template v-slot:group.header="{items, isOpen, toggle}">
<th colspan="12">
<v-sheet>
<div #click="toggle" style="display: inline; width: 100vw">
<v-icon class="mr-3">{{ isOpen ? 'mdi-folder-open' : 'mdi-folder' }}</v-icon>
{{ items[0].folder }}
</div>
<div style="display: inline; float: right;">
<v-icon #click="deleteItem(item)">mdi-dots-vertical</v-icon>
</div>
</v-sheet>
</th>
</template>
```
But I wondering why Vuetify v-spacer is not working

v-spacer is basically just a div with the style flex-grow: 1. So to get this to work the parent container of the v-spacer needs to have display: flex.
In your case you can add this to v-sheet:
<v-sheet class="d-flex">
<!-- content -->
</v-sheet>

Related

Position absolute div hides after going outside of its parent container and into another container

The title almost says it all. But here I provided some examples and a sandbox for you to see what I'm talking about.
Here is a link to the sandbox that I created and added all dependencies.
As you can see in the first picture, my dropdown menu goes behind everything and part of it literally becomes hidden. It's there but not visible
And here is what a dropdown should look like:
I thought to myself that the problem might be because of the z-index. But the element has a z-index: 1000; style applied to it and you can see the applied styles and selected element in chrome inspect in the below picture. As you can see the element's full rectangle is highlighted in inspect but is not visible.
But there is a position: fixed; header with a dropdown menu inside of it which uses react-bootstrap dropdown as well. But it is shown perfectly and without any problems. I gave that header a static position and here is the result. The dropdown was still being shown perfectly.
I've been thinking and searching about this problem for weeks and still couldn't find a solution. Any help will be much appreciated and thank you in advance.
import React from "react";
import DataTable from "react-data-table-component";
import DataTableExtensions from "react-data-table-component-extensions";
import "react-data-table-component-extensions/dist/index.css";
import { Dropdown } from "react-bootstrap";
function UsersAdmin() {
const users = [
{
id: 1,
first_name: "test",
last_name: "test",
email: "test",
mobile: "test",
avatar: "avatar.png",
created_at: "2021-11-15T19:14:44.000000Z",
updated_at: "2021-11-15T19:14:44.000000Z"
},
{
id: 2,
first_name: "test",
last_name: "test",
email: "test",
mobile: "test",
avatar: "avatar.png",
created_at: "2021-11-15T19:14:44.000000Z",
updated_at: "2021-11-15T19:14:44.000000Z"
},
{
id: 3,
first_name: "test",
last_name: "test",
email: "test",
mobile: "test",
avatar: "avatar.png",
created_at: "2021-11-15T19:14:44.000000Z",
updated_at: "2021-11-15T19:14:44.000000Z"
}
];
const columns = [
{
name: "language item",
selector: "id",
sortable: true,
width: "90px"
},
{
name: "language item",
selector: "first_name",
sortable: true
},
{
name: "language item",
selector: "last_name",
sortable: true
},
{
name: "language item",
selector: "email",
sortable: true,
grow: 1.5
},
{
name: "language item",
selector: "mobile",
sortable: true
},
{
name: "language item",
selector: "created_at",
sortable: true,
grow: 2
},
{
name: "language item",
cell: (row) => (
<div style={{ width: 50, height: 50 }}>
<img
src={`${process.env.REACT_APP_BASE_URL}/storage/${row.avatar}`}
style={{ width: "100%", borderRadius: "50%" }}
/>
</div>
),
width: "75px"
},
{
name: "language item",
cell: (row) => (
<Dropdown>
<Dropdown.Toggle size="sm" />
<Dropdown.Menu size="sm">
<Dropdown.Item>
<i className="fa fa-edit mr-1" /> {"language item"}
</Dropdown.Item>
<Dropdown.Item>
<i className="fa fa-download mr-1" /> {"language item"}
</Dropdown.Item>
<Dropdown.Item>
<i className="fa fa-download mr-1" /> {"language item"}
</Dropdown.Item>
<Dropdown.Item>
<i className="mdi mdi-eye mr-1" /> {"language item"}
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
),
width: "75px"
}
];
return (
<>
<div className="container-fluid">
<div className="p-3">
<h2>{"language item"}</h2>
</div>
<div className="card" style={{ width: "100%" }}>
<div className="card-body">
<DataTableExtensions exportHeaders columns={columns} data={users}>
<DataTable
columns={columns}
data={users}
pagination
defaultSortAsc={false}
defaultSortField="updated_at"
noHeader
/>
</DataTableExtensions>
</div>
</div>
</div>
</>
);
}
export default UsersAdmin;
it's not about z-index. Your selection is hiding because the container of your table has overflowY hidden:
.hkMDrI {
position: relative;
width: 100%;
border-radius: inherit;
overflow-x: auto;
overflow-y: hidden;
min-height: 0;
}
So you won't be able to see what is hide outside the height of that div.
Width your current html I don't think it has any solution unless you don't need scrolling with that table, which I doubt.
Changing the html I would place your .dropdown-menuoutside the scrolling div however you will need javaScriptto position the element the right place, under the arrow icon.

Not able to apply styles/class in vue js while using it in loop

Here is my code. item.fav is initially false, but when I click on button it becomes true, I want color of icon to be purple when item.fav is true. But this is not working.
<template>
<v-container fluid>
<v-row>
<v-col v-for="(item, index) in items">
<v-btn icon #click='changeFav(item)'>
<v-icon :style='{color:`${fillcolor(item)}`}'>mdi-heart</v-icon>
</v-btn>
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
....,
methods: {
fillcolor(item){
return item.fav ? 'purple' : 'orange';
},
changeFav(item){
item.fav=true;
},
}
</script>
I have also tried using class
.....
<v-icon class='{fillColor: item.fav}'>mdi-heart</v-icon>
.....
<style>
.fillColor {
color: 'purple';
}
</style>
when using a data variable as condition variable it works, but I cannot use it here as I'm having a loop.
What is that I'm doing wrong here or how can I do it better?
Take a look at Class and Style Bindings
Here is an example
<script setup>
import {ref} from 'vue'
const changeFav = (item) => {
item.fav = !item.fav;
}
const items = ref([
{name: "test1", fav: false},
{name: "test2", fav: false},
{name: "test3", fav: true},
{name: "test4", fav: false},
{name: "test5", fav: false},
{name: "test6", fav: false},
{name: "test7", fav: false},
])
</script>
<template>
<div v-for="(item, index) in items" :key="index" #click="changeFav(item)" >
<span :class="[item.fav ? 'orange' : 'purple']">
{{item.name}}
</span>
</div>
</template>
<style >
.purple {
color: purple;
}
.orange {
color: orange;
}
</style>
The problem with your code is that your fillcolor method needs to run on each click, but instead you are calling the changeFav method which only triggers the item.fav and the fillcolor method never runs.
So, You need to call that function too by changing your code to this:
<v-container fluid>
<v-row>
<v-col v-for="(item, index) in items" :key="index">
<v-btn icon #click="changeFav(item)">
<v-icon :style="{ color: `${fillcolor(item)}` }">mdi-heart</v-icon>
</v-btn>
</v-col>
</v-row>
</v-container>
export default {
....,
methods: {
fillcolor(item) {
return item.fav ? "purple" : "orange";
},
changeFav(item) {
item.fav = true;
return this.fillcolor(item)
},
},
}
================================================================
But I would prefer to do it like this:
<template>
<v-app>
<v-container fluid>
<v-row>
<v-col v-for="(item, index) in items" :key="index">
<v-btn icon #click="item.fav = !item.fav">
<v-icon :color="item.fav ? 'purple': 'red'">mdi-heart</v-icon>
</v-btn>
</v-col>
</v-row>
</v-container>
</v-app>
</template>
Although, it really depends on the way you are handling your data. For example, if you are using vuex then you should consider using mutations. I assumed you have the items and can handle them simply like this:
export default {
data: () => ({
items: [
{ name: "t1", fav: false },
{ name: "t2", fav: false },
{ name: "t3", fav: false },
{ name: "t4", fav: false },
{ name: "t5", fav: false },
{ name: "t6", fav: false },
{ name: "t7", fav: false },
],
}),
};

Best way to apply css class formatting on vuetify data table column

I have dynamically changing data for Vuetify's v-data-table:
<v-data-table
:headers="table_headers"
:items="table_content"
>
</v-data-table>
In the table_headers I can add "class" so that it can be used to apply custom css formatting on the headers:
table_headers: [
{
text: 'Dessert (100g serving)',
align: 'start',
sortable: false,
value: 'name',
class: 'section-dessert'
},
{ text: 'Calories', value: 'calories', class: 'section-calories' },
],
However this only affect headers. So my question is, would it be possible, or what is the best way to apply this class that I keep in table_headers also to all rows (per respective column).
So that when I write css for given class it will affect both header and all the rows for the given column.
EDIT:
So essentially I want to style whole column (on column basis), something like this:
Can I color table columns using CSS without coloring individual cells?
However with dynamic data for headers and table items (and since vuetify does not have working colgroup then Im not sure how to do something like this here)
You have to use the body slot to achieve that kind of customization, see below:
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
table_headers: [{
text: 'Dessert (100g serving)',
align: 'start',
sortable: false,
value: 'name',
class: 'section-dessert'
},
{
text: 'Calories',
value: 'calories',
class: 'section-calories'
},
],
table_content: [{
name: "Some dessert",
calories: 100
},
{
name: "Some dessert2",
calories: 200
},
{
name: "Some dessert3",
calories: 300
},
{
name: "Some dessert4",
calories: 400
}
]
}),
})
.section-dessert {
color: red !important;
}
.section-calories {
color: green !important;
}
<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">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.0/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/babel-polyfill/dist/polyfill.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
<body>
<div id="app">
<v-app>
<v-data-table :headers="table_headers" :items="table_content" hide-action hide-default-footer>
<template v-slot:body="{ items }">
<tbody>
<tr v-for="item in items" >
<td :class="table_headers[0].class">{{item.name}}</td>
<td :class="table_headers[1].class">{{item.calories}}</td>
</tr>
</tbody>
</template>
</v-data-table>
</v-app>
</div>
</body>

How to arrange elements in Vuejs component

I have a component in Vue like this:
Tags.vue
<template>
<div>
<v-chip
v-for="tag in tags"
:key="tag"
class="ma-2"
label
x-small
color="blue"
text-color="white"
>
{{ tag }}
</v-chip>
</div>
</template>
<script>
export default {
name: 'Tags',
props: {
tags: {
type: Array,
default: () => ['Empty'],
},
},
}
</script>
This is the result
How can I make them appear one below the other instead of side by side?
This should be a css questions. Add a class "container" for the wrapper div and put some css into it. You can use flex-box
<div class="container">
<v-chip
v-for="tag in tags"
:key="tag"
class="ma-2"
label
x-small
color="blue"
text-color="white"
>
{{ tag }}
</v-chip>
</div>
<style lang="scss" scoped>
.container{
display: flex,
flex-direction: 'column'
}
</style>
This is another solution without css:
I use tag https://vuetifyjs.com/en/components/chip-groups/
<template>
<div class="container">
<v-chip-group column active-class="primary--text">
<v-chip
v-for="tag in tags"
:key="tag"
class="ma-2"
outlined
x-small
color="blue"
text-color="blue"
>
{{ tag }}
</v-chip>
</v-chip-group>
</div>
</template>
<script>
export default {
name: 'Tags',
props: {
tags: {
type: Array,
default: () => ['Empty'],
},
},
}
</script>

Action button not working using vuetify and vuex

Here, I have setup laravel 6 project with vue and vuetify. I am trying to create crud table where I am trying to fetch data from vuex store but for some I am not able to see my action button. I can see my table with data but action coulmn is empty. I have created store folder and inside my store folder I have Store.js file.
Stage.vue
<template>
<div>
<h1 class="text-xs-center info--text mb-2">{{ message }}</h1>
<v-data-table
:headers="headers"
:items="items"
>
<template slot="items" slot-scope="props">
<td>{{ props.item.code }}</td>
<td class="text-xs-right">{{ props.item.name }}</td>
<td class="text-xs-right">{{ props.item.description }}</td>
<td class="justify-center layout px-0">
<v-btn icon class="mx-0" #click="editItem(props.item)">
<v-icon color="teal">edit</v-icon>
</v-btn>
<v-btn icon class="mx-0" #click="deleteItem(props.item)">
<v-icon color="pink">delete</v-icon>
</v-btn>
</td>
</template>
<template slot="no-data">
<v-alert :value="true" color="error" icon="warning">
Sorry, nothing to display here :(
</v-alert>
</template>
</v-data-table>
<v-dialog v-model="dialog" max-width="500px">
<template v-slot:activator="{ on }">
<v-btn color="error" dark class="mb-2" v-on="on">Add New Stage</v-btn>
</template>
<v-card>
<v-card-title>
<span>{{ formTitle }}</span>
</v-card-title>
<v-card-text>
<v-container grid-list-md>
<v-layout wrap>
<v-flex xs12 sm6 md4>
<v-text-field label="Code" v-model="editedItem.code"></v-text-field>
</v-flex>
<v-flex xs12 sm6 md4>
<v-text-field label="Name" v-model="editedItem.name"></v-text-field>
</v-flex>
<v-flex xs12 sm6 md4>
<v-text-field label="Description" v-model="editedItem.description"></v-text-field>
</v-flex>
</v-layout>
</v-container>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="blue darken-1" text #click.native="close">Cancel</v-btn>
<v-btn color="blue darken-1" text #click.native="save">Save</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
<script>
export default {
name: 'Stage',
props: {
},
data: () => ({
dialog: false,
editedIndex: -1,
editedItem: {
code: '',
name: '',
description: ''
},
defaultItem: {
code: '',
name: '',
description: ''
}
}),
computed: {
message () {
return this.$store.getters.getMessage
},
headers () {
return this.$store.getters.getHeaders
},
items () {
return this.$store.getters.getItems
},
formTitle () {
return this.editedIndex === -1 ? 'New Stage' : 'Edit Stage'
}
},
watch: {
dialog (val) {
val || this.close()
}
},
methods: {
editItem (item) {
this.editedIndex = this.items.indexOf(item)
this.editedItem = Object.assign({}, item)
this.dialog = true
},
deleteItem (item) {
const index = this.items.indexOf(item)
confirm('Are you sure you want to delete this item?') && this.$store.commit('deleteItem', index)
// Todo: Make this delete item from store
},
close () {
this.dialog = false
setTimeout(() => {
this.editedItem = Object.assign({}, this.defaultItem)
this.editedIndex = -1
}, 300)
},
save () {
if (this.editedIndex > -1) {
Object.assign(this.items[this.editedIndex], this.editedItem)
// TODO: Edit item in the store.
this.$store.commit('updateItem', this.editedItem, this.editedIndex)
} else {
this.$store.commit('newItem', this.editedItem)
}
this.close()
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style>
</style>
store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
msg: 'Vuetify table of Vuex state items.',
headers: [
{
text: 'Code',
align: 'left',
sortable: true,
value: 'code'
},
{ text: 'Name', value: 'name' },
{ text: 'Description', value: 'description' },
{ text: 'Actions', value: 'description', sortable: false }
],
items: [
{
value: 'false',
code: 23,
name: 'dsvdf',
description: 'Le Manns'
},
{
value: 'false',
code: 1,
name: 'ddd',
description: 'Le Manns'
}
]
},
mutations: {
newItem (state, payload) {
state.items.push(payload)
},
deleteItem (state, payload) {
state.items.splice(payload, 1)
},
updateItem (state, payload, index) {
state.items[index] = payload
}
},
actions: {
},
getters: {
getMessage (state) {
return state.msg
},
getHeaders (state) {
return state.headers
},
getItems (state) {
return state.items
}
}
})
In the state.headers property of your store, you have:
{ text: 'Actions', value: 'name', sortable: false }
This should be:
{ text: 'Actions', value: 'action', sortable: false }
The value property has the wrong value. Also, you have not assigned the imported headers and items to the v-data-table element. It should be:
<v-data-table
:headers="headers"
:items="items"
>
<!-- ... the rest of your code ... -->
Here is a working codepen.

Resources