DataTable
The VDataTable
component is used for displaying tabular data. Features include sorting, searching, pagination, and row selection.
Usage
Basic Usage
To use the VDataTable
component, you will need to provide it with an array of items to display and an array of headers, which define the columns of the table.
Here is an example of basic usage of the VDataTable
component:
INFO
The VDataTable
component is registered globally when you install with @morpheme/ui
. So you don't need to import it manually.
Striped
To create a striped table, you can use the striped
prop:
This will add alternate background colors to each row of the table, giving it a striped appearance.
Hover
To add a hover effect to the rows of the table, you can use the hover
prop:
This will change the background color of the row when the mouse pointer is hovering over it.
Flat
To remove the box shadow and border of the table, you can use the flat
prop:
Bordered
To add a border around the table, you can use the bordered
prop:
Tile
To remove border radius of the table, you can use the tile
prop:
Dense
To make the rows of the table denser, you can use the dense
prop:
Loading
To display a loading state for the table, you can use the loading
prop. This will display a loading spinner and disable the table until the data is loaded.
Empty
The VDataTable
component can display an empty state when the items prop is an empty array. This can be useful if you want to inform the user that there are no data items to display in the table.
This will display a message indicating that the table is empty.
You can also customize the appearance and behavior of the empty state using the following props:
empty-text
: Customize the text displayed in the empty state.empty-class
: Add a custom class to the empty state element.
For example, to customize the text displayed in the empty state and add a custom class, you can use the following code:
<template>
<VDataTable
:items="[]"
empty-text="No data available"
empty-class="text-red-500"
/>
</template>
<template>
<VDataTable
:items="[]"
empty-text="No data available"
empty-class="text-red-500"
/>
</template>
This will display the empty state with the text "No data available" and the class "text-red-500" applied.
Must Sort
To require that a column be sorted before the table can be displayed, you can use the must-sort
prop:
Disable Sorting
To disable sorting for the table, you can use the disable-sorting
prop:
Custom Wrapper Class
To add a custom class to the wrapper element of the table, you can use the wrapper-class
prop:
Custom Class
To add custom classes to the various components of the table, you can use the following props:
header-class
: Add a custom class to the header element.body-class
: Add a custom class to the body element.footer-class
: Add a custom class to the footer element.column-inactive-class
: Add a custom class to the inactive column elements.hover-class
: Add a custom class to the hover state of the rows.td-class
: Add a custom class to the cells.tr-class
: Add a custom class to the rows.
For example, to add custom classes to the header, body, footer, and rows, you can use the following code:
<template>
<VDataTable
:headers="headers"
:items="items"
header-class="bg-blue-600"
body-class="bg-gray-100"
footer-class="bg-gray-100"
column-inactive-class="text-blue-50 hover:text-blue-200"
hover
hover-class="transition duration-300 hover:bg-blue-500 hover:text-white"
td-class="group-hover:text-white"
tr-class="!hover:bg-gray-700 __TR__CLASS__"
/>
</template>
<template>
<VDataTable
:headers="headers"
:items="items"
header-class="bg-blue-600"
body-class="bg-gray-100"
footer-class="bg-gray-100"
column-inactive-class="text-blue-50 hover:text-blue-200"
hover
hover-class="transition duration-300 hover:bg-blue-500 hover:text-white"
td-class="group-hover:text-white"
tr-class="!hover:bg-gray-700 __TR__CLASS__"
/>
</template>
This will add the classes bg-blue-600
to the header element, bg-gray-100
to the body element, bg-gray-100
to the footer element, transition duration-300 hover:bg-blue-500 hover:text-white
to the hover state of the rows, group-hover:text-white
to the cells, and !hover:bg-gray-700 TR__CLASS
to the rows.
Selecting Rows in the DataTable
The VDataTable
component provides the selectable
prop that allows you to enable row selection in the table. When the selectable
prop is set to true
, a checkbox will be displayed in the first column of each row, and the user will be able to select multiple rows by clicking on the checkboxes.
To use row selection, you will need to bind the v-model
directive to a variable that will store the selected rows. The selected rows will be stored as an array of objects, with each object representing a selected row.
For example, to enable row selection and bind the v-model
directive to a selected
variable, you can use the following code:
This will enable row selection and bind the selected variable to the v-model
directive.
Freezing Columns in the DataTable
The VDataTable
component allows you to freeze columns in place, so that they remain visible while the rest of the table is scrolled horizontally. This can be useful if you want to keep important columns, such as the first and last columns, visible at all times.
To freeze a column, you can use the freeze
prop in the header object for that column. The positionFreeze
prop can be used to specify whether the column should be frozen to the left or right side of the table.
This will freeze the first and last columns of the table, so that they remain visible while the rest of the table is scrolled horizontally.
Server Side
This is an example of using a server-side
data table in Vue.js. The data table is connected to a server-side API to retrieve data and handle pagination, sorting, and filtering.
The example includes a function to convert ratings to star icons and displays them in the table. It also displays thumbnail images and formats price values with the appropriate currency symbol.
Search
The VDataTable
component allows for searching through its items by binding a search string to its search
prop.
In this example, this is done by using the VInput
component to create a search bar that updates the search
prop of the VDataTable
component whenever the user inputs a new search query. The VDataTable
component then filters its items based on this search query.
Props
Name | Type | Default |
---|---|---|
bodyClass | String | '' |
bordered | Boolean | false |
columnActiveClass | String | '' |
columnInactiveClass | String | '' |
dense | Boolean | false |
disableSorting | Boolean | false |
flat | Boolean | false |
footerClass | String | '' |
footerColor | String | '' |
headerClass | String | '' |
headers | Array as PropType<VDataTableHeader[]> | [] |
hideFooter | Boolean | false |
hover | Boolean | false |
hoverClass | String | '' |
items | Array as PropType<VDataTableItem[]> | [] |
itemsPerPage | Number | 10 |
loading | Boolean | false |
loadingText | String | 'Loading...' |
modelValue | Array | [] |
multiSort | Boolean | false |
mustSort | Boolean | false |
noDataText | String | 'No results' |
noShadow | Boolean | false |
page | Number | 1 |
pagination | Object | {} |
rowClass | (item: T, index: number) => string; | undefined |
search | String | '' |
searchBy | [String, Array] as PropType<string> | string[] |
selectable | Boolean | false |
serverSide | Boolean | false |
sortBy | String | '' |
sortDirection | String as PropType<SortDirection> | '' |
striped | Boolean | false |
stripedClass | String | '' |
tdClass | String | '' |
tile | Boolean | false |
totalItems | Number | 0 |
trClass | String | '' |
value | Array | [] |
wrapperClass | String | '' |
Events
update:search
This event is emitted when the search string changes. The value parameter contains the new search string.
Type:
(e: 'update:search', value: string): void;
(e: 'update:search', value: string): void;
update:sortBy
This event is emitted when the sortBy value changes. The value parameter contains the new sortBy value.
Type:
(e: 'update:sortBy', value: string): void;
(e: 'update:sortBy', value: string): void;
update:sortDirection
This event is emitted when the sortDirection value changes. The value parameter contains the new sortDirection value.
Type:
(e: 'update:sortDirection', value: SortDirection): void;
(e: 'update:sortDirection', value: SortDirection): void;
update:page
This event is emitted when the page value changes. The value parameter contains the new page value.
Type:
(e: 'update:page', value: number): void;
(e: 'update:page', value: number): void;
update:itemsPerPage
This event is emitted when the itemsPerPage value changes. The value parameter contains the new itemsPerPage value.
Type:
(e: 'update:itemsPerPage', value: number): void;
(e: 'update:itemsPerPage', value: number): void;
update:totalItems
This event is emitted when the totalItems value changes. The value parameter contains the new totalItems value.
Type:
(e: 'update:totalItems', value: number): void;
(e: 'update:totalItems', value: number): void;
update:pagination
This event is emitted when any of the pagination values (page, items per page, or total items) change. The value
parameter contains an object with the updated pagination values.
Type:
(e: 'update:pagination', value: Record<string, any>): void;
(e: 'update:pagination', value: Record<string, any>): void;
page:change
This event is emitted when the page number changes. The value
parameter contains the new page number.
Type:
(e: 'page:change', value: number): void;
(e: 'page:change', value: number): void;
itemsPerPage:change
This event is emitted when the number of items per page changes. The value
parameter contains the new number of items per page.
Type:
(e: 'itemsPerPage:change', value: number): void;
(e: 'itemsPerPage:change', value: number): void;
pagination:change
This event is emitted when any of the pagination values (page, items per page, or total items) change. The value
parameter contains an object with the updated pagination values.
Type:
(e: 'pagination:change', value: Record<string, any>): void;
(e: 'pagination:change', value: Record<string, any>): void;
update:modelValue
This event is emitted when the value of the v-model
directive changes. The value
parameter contains the new value.
Type:
(e: 'update:modelValue', value: any): void;
(e: 'update:modelValue', value: any): void;
sort
The sort
event is emitted when the user sorts a table by a particular column. The payload for this event includes the name of the column that the table is being sorted by (sortBy
) and the direction of the sort (direction
).
Type:
(e: 'sort', payload: {sortBy: string; direction: SortDirection}): void;
(e: 'sort', payload: {sortBy: string; direction: SortDirection}): void;
row:click
The row:click
event is emitted when the user clicks on a row in the table. The payload for this event includes the data for the clicked row (item
) and the index of the row in the table (index
).
Type:
(e: 'row:click', payload: {item: VDataTableItem; index: number}): void;
(e: 'row:click', payload: {item: VDataTableItem; index: number}): void;
Slots
loading
The loading
slot allows you to customize the content shown when the table is loading data.
Type:
loading?: (props: {}) => any;
loading?: (props: {}) => any;
Example:
<template>
<VDataTable>
<template #loading>Loading...</template>
</VDataTable>
</template>
<template>
<VDataTable>
<template #loading>Loading...</template>
</VDataTable>
</template>
header.selectable
The header.selectable
slot allows you to customize the content of the selectable column in the table header. It receives a selectAll
payload that indicates whether all rows are currently selected.
Type:
'header.selectable'?: (props: {selectAll: boolean}) => any;
'header.selectable'?: (props: {selectAll: boolean}) => any;
Example:
<template>
<VDataTable>
<template #header.selectable="{selectAll}">
<v-checkbox v-model="selectAll" />
</template>
</VDataTable>
</template>
<template>
<VDataTable>
<template #header.selectable="{selectAll}">
<v-checkbox v-model="selectAll" />
</template>
</VDataTable>
</template>
empty
The empty
slot allows you to customize the content shown when the table has no data.
Type:
empty?: (props: {}) => any;
empty?: (props: {}) => any;
Example:
<template>
<VDataTable>
<template #empty> No Data </template>
</VDataTable>
</template>
<template>
<VDataTable>
<template #empty> No Data </template>
</VDataTable>
</template>
item.selected
The item.selected
slot allows you to customize the content of the selected column for each row in the table. It receives a selected
payload that indicates whether the current row is selected.
Type:
'item.selected'?: (props: {selected: any[]; item: T}) => any;
'item.selected'?: (props: {selected: any[]; item: T}) => any;
Example:
<template>
<VDataTable>
<template #item.selected="selected">
<VSwitch v-model="selected" />
</template>
</VDataTable>
</template>
<template>
<VDataTable>
<template #item.selected="selected">
<VSwitch v-model="selected" />
</template>
</VDataTable>
</template>
item.index
The item.index
slot allows you to customize the content of the index column for each row in the table. It receives an index
payload that indicates the index of the current row.
Type:
'item.index'?: (props: {index: number; item: T}) => any;
'item.index'?: (props: {index: number; item: T}) => any;
Example:
<template>
<VDataTable>
<template #item.index="{index}">
{{ index }}
</template>
</VDataTable>
</template>
<template>
<VDataTable>
<template #item.index="{index}">
{{ index }}
</template>
</VDataTable>
</template>
item.{value}
The item.{value}
slot allows you to customize the content of a particular column for each row in the table. The value in the slot name corresponds to the name of the column. It receives an item
payload that contains the data for the current row and an index
payload that indicates the index of the current row.
Type:
[K in keyof T as K extends string ? `item.${K}` : never]?: (props: {
item: T;
index: number;
}) => any;
[K in keyof T as K extends string ? `item.${K}` : never]?: (props: {
item: T;
index: number;
}) => any;
Example:
<template>
<VDataTable>
<template #item.id="{item}">
{{ item.id }}
</template>
<template #item.fullName="{item}">
{{ item.firstName }} {{ item.lastName }}
</template>
<template #item.website="{item}">
<Link :to="item.website"> {{ item.website }} </Link>
</template>
</VDataTable>
</template>
<template>
<VDataTable>
<template #item.id="{item}">
{{ item.id }}
</template>
<template #item.fullName="{item}">
{{ item.firstName }} {{ item.lastName }}
</template>
<template #item.website="{item}">
<Link :to="item.website"> {{ item.website }} </Link>
</template>
</VDataTable>
</template>
header.{value}
The header.{value}
slot allows you to customize the content of a table header (th
).
Type:
{
[K in keyof T as K extends string ? `header.${K}` : never]?: (props: {
header: H;
index: number;
}) => any;
}
{
[K in keyof T as K extends string ? `header.${K}` : never]?: (props: {
header: H;
index: number;
}) => any;
}
Example:
<script setup lang="ts">
import {items, headers} from './data';
</script>
<template>
<VDataTable :headers="headers" :items="items">
<template #header.name="{header}">
<div class="uppercase">{{ header.text }}</div>
</template>
</VDataTable>
</template>
<script setup lang="ts">
import {items, headers} from './data';
</script>
<template>
<VDataTable :headers="headers" :items="items">
<template #header.name="{header}">
<div class="uppercase">{{ header.text }}</div>
</template>
</VDataTable>
</template>
footer
The footer
slot allows you to customize the content of the table footer. It receives a pagination
payload that indicates whether pagination is enabled, a perPage
payload that indicates the number of items per page, a serverSide
payload that indicates whether the table is using server-side data, an items
payload that contains the data for the current page, a totalItems
payload that indicates the total number of items in the table, a footerColor
payload that indicates the color of the footer, a footerClass
payload that indicates the class of the footer, and a page
payload that indicates the current page.
Type:
footer?: (props: {
pagination: VDataTablePaginationProps;
perPage: number;
serverSide: boolean;
items: T[];
totalItems: number;
footerColor: string;
footerClass: string;
page: number;
}) => any;
footer?: (props: {
pagination: VDataTablePaginationProps;
perPage: number;
serverSide: boolean;
items: T[];
totalItems: number;
footerColor: string;
footerClass: string;
page: number;
}) => any;
Example:
<template>
<VDataTable>
<template #footer="{page}">
<VPagination v-model="page" />
</template>
</VDataTable>
</template>
<template>
<VDataTable>
<template #footer="{page}">
<VPagination v-model="page" />
</template>
</VDataTable>
</template>
header
The header
slot alows you to customize the content of header (th
) of table.
Type:
header: (props: {
headerClass: string;
headers: H[];
sortMap: Map<string, SortDirection>;
selectable: boolean;
disableSorting: boolean;
handleSort: (header: H) => void;
}) => any;
header: (props: {
headerClass: string;
headers: H[];
sortMap: Map<string, SortDirection>;
selectable: boolean;
disableSorting: boolean;
handleSort: (header: H) => void;
}) => any;
body
The body
slot alows you to customize the content of table body (tbody
) of table.
Type:
body: (props: {
bodyClass: string;
items: T[];
loading: boolean;
headers: H[];
noDataText: string;
striped: boolean;
hover: boolean;
trClass: string;
selected: T[];
get: (item: T, key: string) => any;
handleRowClick: (item: T, index: number) => void;
getTdClass: typeof getTdClass;
loadingText: string;
stripedClass: string;
hoverClass: string;
tdClass: string;
selectable: boolean;
start: number;
}) => any;
body: (props: {
bodyClass: string;
items: T[];
loading: boolean;
headers: H[];
noDataText: string;
striped: boolean;
hover: boolean;
trClass: string;
selected: T[];
get: (item: T, key: string) => any;
handleRowClick: (item: T, index: number) => void;
getTdClass: typeof getTdClass;
loadingText: string;
stripedClass: string;
hoverClass: string;
tdClass: string;
selectable: boolean;
start: number;
}) => any;
item
The item
slot alows you to customize the content of table row (tr
) of table.
Type:
item: (props: {
item: T;
index: number;
headers: H[];
striped: boolean;
hover: boolean;
trClass: string;
selected: T[];
get: (item: T, key: string) => any;
handleRowClick: (item: T, index: number) => void;
getTdClass: typeof getTdClass;
}) => any;
item: (props: {
item: T;
index: number;
headers: H[];
striped: boolean;
hover: boolean;
trClass: string;
selected: T[];
get: (item: T, key: string) => any;
handleRowClick: (item: T, index: number) => void;
getTdClass: typeof getTdClass;
}) => any;
Example:
<template>
<VDataTable>
<template #item="{
item,
handleRowClick,
headers,
getTdClass,
tdClass,
get
}">
<tr
@click="handleRowClick(item)"
>
<td
v-for="(header, index) in headers"
:key="index"
class="v-table-td"
:class="[
getTdClass(header),
tdClass,
header?.tdClass || '',
]"
>
{{ get(item, header.value) }}
</td>
</tr>
</template>
</VDataTable>
</template>
<template>
<VDataTable>
<template #item="{
item,
handleRowClick,
headers,
getTdClass,
tdClass,
get
}">
<tr
@click="handleRowClick(item)"
>
<td
v-for="(header, index) in headers"
:key="index"
class="v-table-td"
:class="[
getTdClass(header),
tdClass,
header?.tdClass || '',
]"
>
{{ get(item, header.value) }}
</td>
</tr>
</template>
</VDataTable>
</template>
CSS Variables
:root {
/* spacing */
--v-table-padding-x: var(--size-spacing-6);
--v-table-padding-y: var(--size-spacing-3);
--v-table-border-color: var(--color-gray-200);
/* thead */
--v-table-thead-bg-color: var(--color-gray-50);
/* th */
--v-table-th-color: var(--color-gray-600);
--v-table-th-font-size: var(--size-font-xs);
--v-table-th-font-weight: var(--font-weight-bold);
--v-table-th-white-space: nowrap;
--v-table-th-text-align: left;
/* th active */
--v-table-th-active-color: var(--color-primary-500);
/* th active hover */
--v-table-th-active-hover-color: var(--color-primary-600);
// tr
--v-table-tr-bg-color: transparent;
/* td */
--v-table-td-color: var(--color-gray-900);
--v-table-td-bg-color: var(--color-white);
--v-table-td-font-size: var(--size-font-sm);
--v-table-td-font-weight: var(--font-weight-regular);
--v-table-td-white-space: nowrap;
--v-table-td-text-align: left;
/* dense */
--v-table-dense-padding-x: var(--size-spacing-4);
--v-table-dense-padding-y: var(--size-spacing-2);
/* striped */
--v-table-striped-bg-color: var(--color-gray-50);
--v-table-striped-even-bg-color: var(--color-white);
/* hover */
--v-table-hover-bg-color: var(--color-gray-100);
}
:root {
/* spacing */
--v-table-padding-x: var(--size-spacing-6);
--v-table-padding-y: var(--size-spacing-3);
--v-table-border-color: var(--color-gray-200);
/* thead */
--v-table-thead-bg-color: var(--color-gray-50);
/* th */
--v-table-th-color: var(--color-gray-600);
--v-table-th-font-size: var(--size-font-xs);
--v-table-th-font-weight: var(--font-weight-bold);
--v-table-th-white-space: nowrap;
--v-table-th-text-align: left;
/* th active */
--v-table-th-active-color: var(--color-primary-500);
/* th active hover */
--v-table-th-active-hover-color: var(--color-primary-600);
// tr
--v-table-tr-bg-color: transparent;
/* td */
--v-table-td-color: var(--color-gray-900);
--v-table-td-bg-color: var(--color-white);
--v-table-td-font-size: var(--size-font-sm);
--v-table-td-font-weight: var(--font-weight-regular);
--v-table-td-white-space: nowrap;
--v-table-td-text-align: left;
/* dense */
--v-table-dense-padding-x: var(--size-spacing-4);
--v-table-dense-padding-y: var(--size-spacing-2);
/* striped */
--v-table-striped-bg-color: var(--color-gray-50);
--v-table-striped-even-bg-color: var(--color-white);
/* hover */
--v-table-hover-bg-color: var(--color-gray-100);
}
Standalone Installation
You can also install the DataTable
component individually via @morpheme/table
package:
npm i @morpheme/table
npm i @morpheme/table
<script setup lang="ts">
import VDataTable from '@morpheme/table';
</script>
<template>
<VDataTable />
</template>
<script setup lang="ts">
import VDataTable from '@morpheme/table';
</script>
<template>
<VDataTable />
</template>
Storybook
View Storybook documentation here.